openapi: 3.1.0
info:
  title: 'BambooHR API'
  description: 'BambooHR API documentation. https://www.bamboohr.com/api/documentation/'
  version: '1.0'
servers:
  -
    url: 'https://{companyDomain}.bamboohr.com'
    variables:
      companyDomain:
        default: companySubDomain
        description: 'Company domain'
paths:
  /api/v1/alert-configurations:
    get:
      tags:
        - 'Alert Configurations'
        - 'Public API'
      summary: 'List alert configurations'
      description: 'Returns all company alert configurations for the authenticated company.'
      operationId: 6d0a073cbf3e97fe0409de42c68fe779
      responses:
        '200':
          description: 'List of alert configurations'
          content:
            application/json:
              schema:
                title: AlertConfigurationListResponse
                type: array
                items:
                  $ref: '#/components/schemas/CompanyAlertDataObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - alerts
    post:
      tags:
        - 'Alert Configurations'
        - 'Public API'
      summary: 'Create an alert configuration'
      description: 'Creates a new company alert configuration.'
      operationId: a05e93bc2eb9c39a40fbd71e6e07f3c6
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CompanyAlertDataObject'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CompanyAlertDataObject'
        '400':
          description: 'Bad request'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - alerts.write
  '/api/v1/alert-configurations/{id}':
    get:
      tags:
        - 'Alert Configurations'
        - 'Public API'
      summary: 'Get an alert configuration'
      description: 'Returns a single company alert configuration by ID.'
      operationId: 14e66bfb5f075043221ce1e843c97493
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Alert configuration'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CompanyAlertDataObject'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - alerts
    put:
      tags:
        - 'Alert Configurations'
        - 'Public API'
      summary: 'Update an alert configuration'
      description: 'Updates an existing company alert configuration.'
      operationId: eb42aa2fa339ba5c08b147fc13c6a79e
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CompanyAlertDataObject'
      responses:
        '200':
          description: Updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CompanyAlertDataObject'
        '400':
          description: 'Bad request'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - alerts.write
  /api/v1/alerts:
    get:
      tags:
        - Alert
        - 'Public API'
      summary: 'List alert templates'
      description: 'Returns all available alert templates with their IDs, names, and group names. Use the returned IDs when creating or updating company alert configurations.'
      operationId: 0f0dcb585e5883175b6557c16cf6755a
      responses:
        '200':
          description: 'List of alert templates'
          content:
            application/json:
              schema:
                title: AlertTemplateListResponse
                properties:
                  alerts: { type: array, items: { properties: { id: { description: 'Alert template ID (bambooAlertId)', type: integer }, name: { description: 'Display name of the alert', type: string }, groupName: { description: 'Category group the alert belongs to', type: string } }, type: object } }
                type: object
        '403':
          description: Forbidden
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - alerts
  /api/v1/compensation/equity/settings:
    get:
      tags:
        - Compensation
        - 'Public API'
      summary: 'Get company equity settings'
      description: 'Retrieves company-level equity settings including calculation type, valuation, shares, pricing, and vesting-related configuration.'
      operationId: db49fb29f9f04d59afad7c01ce860418
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                title: CompensationEquitySettingsResponse
                properties:
                  calculationType: { type: string }
                  companyValuation: { oneOf: [{ type: number }, { type: 'null' }] }
                  outstandingShares: { oneOf: [{ type: integer }, { type: 'null' }] }
                  pricePerShare: { oneOf: [{ type: number }, { type: 'null' }] }
                  currencyCode: { type: string }
                  sliderMin: { oneOf: [{ type: integer }, { type: 'null' }] }
                  sliderMax: { oneOf: [{ type: integer }, { type: 'null' }] }
                  vestingConditions: { oneOf: [{ type: string }, { type: 'null' }] }
                  disclaimers: { oneOf: [{ type: string }, { type: 'null' }] }
                type: object
                additionalProperties: true
        '403':
          description: Forbidden
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - equity
    put:
      tags:
        - Compensation
        - 'Public API'
      summary: 'Update company equity settings'
      description: 'Updates company-level equity settings including calculation type, valuation, shares, pricing, and vesting-related configuration.'
      operationId: c5880b509783cd9d7fce9ddf5d6af1be
      requestBody:
        required: true
        content:
          application/json:
            schema:
              title: CompensationEquitySettingsUpdateRequest
              properties:
                calculationType:
                  type: string
                companyValuation:
                  oneOf: [{ type: number }, { type: 'null' }]
                outstandingShares:
                  oneOf: [{ type: integer }, { type: 'null' }]
                pricePerShare:
                  oneOf: [{ type: number }, { type: 'null' }]
                currencyCode:
                  type: string
                sliderMin:
                  oneOf: [{ type: integer }, { type: 'null' }]
                sliderMax:
                  oneOf: [{ type: integer }, { type: 'null' }]
                vestingConditions:
                  oneOf: [{ type: string }, { type: 'null' }]
                disclaimers:
                  oneOf: [{ type: string }, { type: 'null' }]
              type: object
              additionalProperties: true
      responses:
        '200':
          description: 'Settings updated successfully'
          content:
            application/json:
              schema:
                title: CompensationEquitySettingsResponse
                properties:
                  calculationType: { type: string }
                  companyValuation: { oneOf: [{ type: number }, { type: 'null' }] }
                  outstandingShares: { oneOf: [{ type: integer }, { type: 'null' }] }
                  pricePerShare: { oneOf: [{ type: number }, { type: 'null' }] }
                  currencyCode: { type: string }
                  sliderMin: { oneOf: [{ type: integer }, { type: 'null' }] }
                  sliderMax: { oneOf: [{ type: integer }, { type: 'null' }] }
                  vestingConditions: { oneOf: [{ type: string }, { type: 'null' }] }
                  disclaimers: { oneOf: [{ type: string }, { type: 'null' }] }
                type: object
                additionalProperties: true
        '400':
          description: 'Bad request'
        '403':
          description: Forbidden
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - equity.write
  /api/v1/compensation/planning_cycles:
    get:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'List compensation planning cycles'
      description: 'List all compensation planning cycles for the company, including status and employee counts.'
      operationId: b65f246186b41a9783a9397c11c703b4
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                title: CompensationPlanningCyclesListResponse
                type: object
                additionalProperties: true
        '403':
          description: Forbidden
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - planning_cycles
    post:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'Create compensation planning cycle'
      description: 'Create a new compensation planning cycle with default settings.'
      operationId: e2ac4e1535f296cb8901f209e04caa83
      responses:
        '201':
          description: 'Cycle created successfully'
          content:
            application/json:
              schema:
                title: CompensationPlanningCycleCreateResponse
                type: object
                additionalProperties: true
        '400':
          description: 'Invalid request data'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - planning_cycles.write
  '/api/v1/compensation/planning_cycles/{id}':
    get:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'Get compensation planning cycle details'
      description: 'Get compensation planning cycle details and currency settings for a cycle.'
      operationId: 5c2b55158b0950b1e9211655666645b6
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                title: CompensationPlanningCycleDetailsResponse
                type: object
                additionalProperties: true
        '403':
          description: Forbidden
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - planning_cycles
    put:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'Update compensation planning cycle'
      description: 'Update compensation planning cycle details and currency settings.'
      operationId: 100b0cf8c5207b35697ff10370fd5fe1
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        description: 'Cycle details and currency settings to update'
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DetailsAndCurrencyRequestDataObject'
      responses:
        '200':
          description: 'Cycle updated successfully'
          content:
            application/json:
              schema:
                title: CompensationPlanningCycleUpdateResponse
                type: object
                additionalProperties: true
        '400':
          description: 'Invalid request data'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Resource not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - planning_cycles.write
    delete:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'Delete compensation planning cycle'
      description: 'Delete a compensation planning cycle.'
      operationId: 22ad75be25455279e2987c80851af5fc
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '204':
          description: 'Cycle deleted successfully'
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Resource not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - planning_cycles.write
  '/api/v1/compensation/planning_cycles/{id}/summary':
    get:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'Get compensation planning cycle summary'
      description: 'Get compensation planning cycle summary (progress, stats).'
      operationId: 9bc279d788f6e86b4cd8b2e0d3de91b1
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                title: CompensationPlanningCycleSummaryResponse
                type: object
                additionalProperties: true
        '400':
          description: 'Bad request'
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - planning_cycles
  '/api/v1/compensation/planning_cycles/{id}/employees':
    get:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'List employees in compensation planning cycle'
      description: 'List employees in the compensation planning cycle (picker data: company roster and membership).'
      operationId: a6b8da1348a3151fe95adc03aaf64447
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                title: CompensationPlanningCycleEmployeesResponse
                type: object
                additionalProperties: true
        '403':
          description: Forbidden
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - planning_cycles
    post:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'Add employees to cycle'
      description: 'Add employees to a compensation planning cycle.'
      operationId: 3958585c861325ea7a2cd30a8c74f042
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        description: 'Employee IDs to add'
        required: true
        content:
          application/json:
            schema:
              required:
                - employeeIds
              properties:
                employeeIds:
                  description: 'Array of employee IDs to add to the cycle'
                  type: array
                  items: { type: string }
              type: object
      responses:
        '204':
          description: 'Employees added successfully'
        '400':
          description: 'Invalid request data'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Resource not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - planning_cycles.write
    delete:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'Remove employees from cycle'
      description: 'Remove employees from a compensation planning cycle.'
      operationId: 89a5068111ec499135c7d6e9a53d5a30
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        description: 'Employee IDs to remove'
        required: true
        content:
          application/json:
            schema:
              required:
                - employeeIds
              properties:
                employeeIds:
                  description: 'Array of employee IDs to remove from the cycle'
                  type: array
                  items: { type: string }
              type: object
      responses:
        '204':
          description: 'Employees removed successfully'
        '400':
          description: 'Invalid request data'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Resource not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - planning_cycles.write
  '/api/v1/compensation/planning_cycles/{id}/budgets':
    get:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'Get compensation planning cycle budgets'
      description: 'Get budget guidelines and breakdown for the compensation planning cycle.'
      operationId: 7efceaee2c010f88244dd01ee81e6e7b
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                title: CompensationPlanningCycleBudgetsResponse
                type: object
                additionalProperties: true
        '403':
          description: Forbidden
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - planning_cycles
  '/api/v1/compensation/planning_cycles/{id}/approvals':
    get:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'Get compensation planning approval flows'
      description: 'Get approval flows for the compensation planning cycle (grouped by department by default).'
      operationId: 4e886b18264480611f380805301c49c4
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
        -
          name: groupBy
          in: query
          description: 'Optional grouping for flows (e.g. department)'
          required: false
          schema:
            type: string
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                title: CompensationPlanningCycleApprovalsResponse
                type: object
                additionalProperties: true
        '403':
          description: Forbidden
        '404':
          description: 'Cycle not found'
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - planning_cycles
  '/api/v1/compensation/planning_cycles/{id}/admins':
    get:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'List compensation planning cycle admins'
      description: 'List compensation planning cycle admins.'
      operationId: b3c51254de6918637a971fe4af382a53
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                title: CompensationPlanningCycleAdminsResponse
                properties:
                  admins: { description: 'List of admins for the cycle', type: array, items: { properties: { employeeId: { description: 'ID of the employee associated with this admin. Null if user has no associated employee.', oneOf: [{ type: string }, { type: 'null' }] }, displayName: { description: 'Display name of the admin', type: string }, firstName: { description: 'First name of the admin', type: string }, lastName: { description: 'Last name of the admin', type: string }, jobTitle: { description: 'Job title of the admin', type: string }, photoUrl: { description: 'URL to the admin profile photo', type: string }, adminType: { description: 'Type of admin', type: string, enum: [FULL_ACCOUNT_ADMIN, CYCLE_ADMIN] }, isRemovable: { description: 'Whether this admin can be removed from the cycle', type: boolean }, addedAt: { description: 'ISO 8601 timestamp when the admin was added. Null for full account admins.', oneOf: [{ type: string, format: date-time }, { type: 'null' }] }, addedByEmployeeId: { description: 'ID of the employee who added this admin. Null for full account admins or if user has no associated employee.', oneOf: [{ type: string }, { type: 'null' }] } }, type: object } }
                  isFullAdmin: { description: 'Whether the requesting user is a full compensation plan admin', type: boolean }
                type: object
        '403':
          description: Forbidden
        '404':
          description: 'Not found'
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - planning_cycles
    post:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'Add cycle admins'
      description: 'Add one or more cycle admins by employee IDs.'
      operationId: c7c32ed5278ac67e2e518bf7484a75dc
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - employeeIds
              properties:
                employeeIds:
                  description: 'Array of employee IDs to add as cycle admins'
                  type: array
                  items: { type: string }
                notifyAdmins:
                  description: 'Whether to send email notifications to new admins'
                  type: boolean
              type: object
      responses:
        '200':
          description: 'Result of adding cycle admins'
          content:
            application/json:
              schema:
                title: AddCycleAdminsResponse
                properties:
                  added: { type: array, items: { properties: { employeeId: { type: string }, displayName: { type: string } }, type: object } }
                  skipped: { type: array, items: { properties: { employeeId: { type: string }, reason: { type: string, enum: [ALREADY_ADMIN, EMPLOYEE_NOT_FOUND, FULL_ACCOUNT_ADMIN] } }, type: object } }
                type: object
        '403':
          description: Forbidden
        '404':
          description: 'Not found'
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - planning_cycles.write
  '/api/v1/compensation/planning_cycles/{id}/change_comm':
    get:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'Get change communication letter details'
      description: 'Get change communication letter details for the compensation planning cycle.'
      operationId: d6987e300672a00c7cfe59afebb64156
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                title: CompensationPlanningCycleChangeCommResponse
                type: object
                additionalProperties: true
        '403':
          description: Forbidden
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - planning_cycles
  '/api/v1/compensation/planning_cycles/{id}/worksheet':
    get:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'Get compensation planning cycle worksheet'
      description: 'Get compensation planning worksheet details for the cycle (recommendations, approvals, or overview context). Omit `type` to use the default view for the current user.'
      operationId: 329acecaa6df729733d0752aa9f6b204
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
        -
          name: type
          in: query
          description: 'Worksheet context: recommendations, approvals, or overview (overview requires cycle admin).'
          required: false
          schema:
            type: string
            enum:
              - recommendations
              - approvals
              - overview
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                title: CompensationPlanningCycleWorksheetResponse
                type: object
                additionalProperties: true
        '400':
          description: 'Bad request'
        '403':
          description: Forbidden
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - planning_cycles
  '/api/v1/compensation/planning_cycles/{id}/worksheet/export':
    get:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'Export compensation planning cycle worksheet to CSV'
      description: 'Download compensation planning worksheet data as a UTF-8 CSV (Excel-friendly BOM). Same data scope as GET worksheet; omit `type` for the default view.'
      operationId: 593d5bff120edf2a218a92022a682728
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
        -
          name: type
          in: query
          description: 'Worksheet context: recommendations, approvals, or overview (overview requires cycle admin).'
          required: false
          schema:
            type: string
            enum:
              - recommendations
              - approvals
              - overview
      responses:
        '200':
          description: 'CSV file download'
          content:
            text/csv:
              schema:
                title: CompensationPlanningCycleWorksheetCsvExport
                type: string
                format: binary
        '400':
          description: 'Bad request'
        '403':
          description: Forbidden
        '404':
          description: 'Worksheet not found'
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - planning_cycles
  '/api/v1/compensation/planning_cycles/{id}/launch':
    put:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'Launch compensation planning cycle'
      description: 'Launch a compensation planning cycle. Validates all cycle data and sets status to Live.'
      operationId: 3a19f07aa737dc826ba43b9a1c1cd257
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '204':
          description: 'Cycle launched successfully'
        '400':
          description: 'Invalid request data'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Resource not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '409':
          description: 'Cycle is not ready to launch'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - planning_cycles.write
  '/api/v1/compensation/planning_cycles/{id}/complete':
    put:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'Complete compensation planning cycle'
      description: 'Complete a compensation planning cycle and finalize employee compensation records.'
      operationId: f4b431363af6573af46750f32632e88b
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Cycle completed successfully'
          content:
            application/json:
              schema:
                title: CompensationPlanningCycleCompleteResponse
                properties:
                  cycleId: { type: integer }
                  updatedEmployeesCount: { type: integer }
                  cycleClosedEmailError: { description: 'Present when cycle completed successfully but sending closed-cycle emails failed', oneOf: [{ type: string }, { type: 'null' }] }
                  errors: { type: array, items: { properties: { employeeId: { type: string }, message: { type: string } }, type: object } }
                type: object
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Resource not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '409':
          description: 'Cycle cannot be completed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Employees with pay rate errors'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - planning_cycles.write
  '/api/v1/compensation/planning_cycles/{id}/budgets/guidelines':
    put:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'Save budget guidelines'
      description: 'Save budget guidelines for a compensation planning cycle.'
      operationId: dacd313af2106213fc4696175941ce65
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        description: 'Budget guideline settings'
        required: true
        content:
          application/json:
            schema:
              properties:
                useGuidelines:
                  type: boolean
                softMin:
                  type: number
                softMax:
                  type: number
              type: object
      responses:
        '200':
          description: 'Guidelines saved successfully'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BudgetGuidelinesView'
        '400':
          description: 'Invalid request data'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Resource not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - planning_cycles.write
  '/api/v1/compensation/planning_cycles/{id}/budgets/breakdown':
    put:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'Save budget breakdown'
      description: 'Save budget breakdown for a compensation planning cycle.'
      operationId: 1d64402ee192568adbd5e3179a91e6e2
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        description: 'Budget breakdown data'
        required: true
        content:
          application/json:
            schema:
              type: array
              items:
                properties:
                  id: { type: string }
                  employeeId: { type: string }
                  budgetAllocations: { properties: { salaryIncrease: { type: number }, bonuses: { type: number }, equity: { type: number } }, type: object }
                type: object
      responses:
        '204':
          description: 'Budget breakdown saved successfully'
        '400':
          description: 'Invalid request data'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Resource not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - planning_cycles.write
  '/api/v1/compensation/planning_cycles/{id}/budgets/import':
    post:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'Import budget breakdown'
      description: 'Import budget breakdown from structured data and apply to allocations.'
      operationId: 1d1fc0f164cb51973a0206b8e2fb2d2d
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        description: 'Budget breakdown import data'
        required: true
        content:
          application/json:
            schema:
              required:
                - budgetBreakdown
              properties:
                budgetBreakdown:
                  type: array
                  items: { properties: { employeeName: { type: string }, emailAddress: { type: string }, salaryBudget: { oneOf: [{ type: number }, { type: 'null' }] }, bonusBudget: { oneOf: [{ type: number }, { type: 'null' }] }, equityBudget: { oneOf: [{ type: number }, { type: 'null' }] } }, type: object }
              type: object
      responses:
        '200':
          description: 'Budget breakdown imported successfully'
          content:
            application/json:
              schema:
                title: BudgetBreakdownImportResponse
                properties:
                  message: { type: string }
                type: object
        '400':
          description: 'Invalid request data'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Resource not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - planning_cycles.write
  '/api/v1/compensation/planning_cycles/{id}/approvals/{templateId}':
    put:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'Update approval flow'
      description: 'Update an approval flow for a compensation planning cycle. Allows modifying recommenders and approvers for a specific approval flow template.'
      operationId: cf87b8e09a001b6fb81dfce6c20ab9e3
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
        -
          name: templateId
          in: path
          required: true
          schema:
            type: string
        -
          name: groupBy
          in: query
          description: 'Optional grouping for returned flows (e.g. department)'
          required: false
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - assignees
                - flowData
              properties:
                assignees:
                  description: 'Array of assignee employee IDs'
                  type: array
                  items: { type: string }
                flowData:
                  description: 'Flow data for the approval'
                  type: object
                  additionalProperties: true
              type: object
      responses:
        '200':
          description: 'Updated approval flows for the cycle'
          content:
            application/json:
              schema:
                title: UpdateApprovalFlowResponse
                type: object
                additionalProperties: true
        '400':
          description: 'Bad request'
        '403':
          description: Forbidden
        '404':
          description: 'Not found'
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - planning_cycles.write
  '/api/v1/compensation/planning_cycles/{id}/approvals/final_approver/{employeeId}':
    post:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'Set final approver'
      description: 'Set or update the final approver for all approval flows in a cycle.'
      operationId: 5c4aab35a34f5760ec044104b5232bf5
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
        -
          name: employeeId
          in: path
          required: true
          schema:
            type: string
        -
          name: groupBy
          in: query
          description: 'Optional grouping for returned flows (e.g. department)'
          required: false
          schema:
            type: string
      responses:
        '200':
          description: 'Updated approval flows for the cycle'
          content:
            application/json:
              schema:
                title: SetFinalApproverResponse
                type: object
                additionalProperties: true
        '400':
          description: 'Bad request'
        '403':
          description: Forbidden
        '404':
          description: 'Not found'
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - planning_cycles.write
  '/api/v1/compensation/planning_cycles/{id}/approvals/employee/{employeeId}':
    delete:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'Remove from approval flow'
      description: 'Remove an employee from all approval chains for a specific cycle.'
      operationId: b1e467e0eef72350eec61fcfeaf4e19d
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
        -
          name: employeeId
          in: path
          required: true
          schema:
            type: string
        -
          name: groupBy
          in: query
          description: 'Optional grouping for returned flows (e.g. department)'
          required: false
          schema:
            type: string
      responses:
        '200':
          description: 'Updated approval flows after removal'
          content:
            application/json:
              schema:
                title: RemoveFromApprovalFlowResponse
                type: object
                additionalProperties: true
        '403':
          description: Forbidden
        '404':
          description: 'Not found'
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - planning_cycles.write
  '/api/v1/compensation/planning_cycles/{id}/recommendations':
    post:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'Save recommendations'
      description: 'Save compensation recommendations for an employee in a cycle.'
      operationId: f3883a522dadbe9e11b34f8b656e3adb
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - employeeId
                - assigneeEmployeeId
                - approvalStage
              properties:
                employeeId:
                  description: 'The ID of the employee receiving the recommendation'
                  type: string
                assigneeEmployeeId:
                  description: 'The ID of the employee making the recommendation'
                  type: string
                approvalFlowId:
                  description: 'The ID of the approval flow'
                  type: integer
                approvalStage:
                  description: 'The approval stage'
                  type: integer
                  enum: [0, 1, 2, 3, 4, 5, 999]
                recSalaryIncrease:
                  properties: { amount: { type: number }, type: { type: string, enum: [currency, percentage] } }
                  oneOf: [{ type: object }, { type: 'null' }]
                recBonusIncrease:
                  properties: { amount: { type: number }, type: { type: string, enum: [currency, percentage] } }
                  oneOf: [{ type: object }, { type: 'null' }]
                recEquityIncrease:
                  properties: { amount: { type: number }, type: { type: string, enum: [currency, percentage] } }
                  oneOf: [{ type: object }, { type: 'null' }]
              type: object
      responses:
        '200':
          description: 'Saved recommendation'
          content:
            application/json:
              schema:
                title: SaveRecommendationResponse
                type: object
                additionalProperties: true
        '400':
          description: 'Bad request'
        '403':
          description: Forbidden
        '404':
          description: 'Not found'
        '409':
          description: Conflict
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - planning_cycles.write
  '/api/v1/compensation/planning_cycles/{id}/recommendations/send':
    post:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'Send recommendations to next stage'
      description: 'Send recommendations to the next approval stage for a specific approval flow template.'
      operationId: a05b6d5f564f805d688ff2c1e37c3990
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - templateId
                - assigneeEmployeeId
                - approvalStage
              properties:
                templateId:
                  description: 'The ID of the template'
                  type: string
                assigneeEmployeeId:
                  description: 'The ID of the assignee employee'
                  type: string
                approvalStage:
                  description: 'The current approval stage'
                  type: integer
              type: object
      responses:
        '200':
          description: 'Recommendations sent successfully'
          content:
            application/json:
              schema:
                title: SendRecommendationsResponse
                properties:
                  message: { type: string }
                type: object
        '400':
          description: 'Bad request'
        '403':
          description: Forbidden
        '404':
          description: 'Not found'
        '409':
          description: Conflict
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - planning_cycles.write
  '/api/v1/compensation/planning_cycles/{id}/admins/{employeeId}':
    delete:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'Remove cycle admin'
      description: 'Remove a cycle admin by employee ID. Full account admins cannot be removed.'
      operationId: ef7619b0ee4c8dc079aaea870cfbe81b
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
        -
          name: employeeId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: 'Admin removed - current user removed themselves'
          content:
            application/json:
              schema:
                title: RemoveCycleAdminSelfResponse
                properties:
                  selfRemoved: { type: boolean }
                type: object
        '204':
          description: 'Cycle admin successfully removed'
        '403':
          description: Forbidden
        '404':
          description: 'Not found'
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - planning_cycles.write
  '/api/v1/compensation/planning_cycles/{id}/change_comm/template':
    put:
      tags:
        - 'Compensation Planning'
        - 'Public API'
      summary: 'Save change comm template'
      description: 'Save the change communication email template for a compensation planning cycle.'
      operationId: c79f9c5950f983e59d2626faa30c00a1
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - messageText
                - subjectText
              properties:
                messageText:
                  description: 'The email body template text'
                  type: string
                subjectText:
                  description: 'The email subject template text'
                  type: string
              type: object
      responses:
        '200':
          description: 'Template saved successfully'
          content:
            application/json:
              schema:
                title: SaveChangeCommTemplateResponse
                properties:
                  message: { type: string }
                type: object
        '400':
          description: 'Bad request'
        '403':
          description: Forbidden
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - planning_cycles.write
  /api/v1/compensation/tools:
    get:
      tags:
        - Compensation
        - 'Public API'
      summary: 'List available compensation tools'
      description: 'Returns the list of available compensation tools/settings for the company, including Levels & Bands, Compensation Benchmarking, Compensation Planning, and Total Rewards. Also returns upsell information if applicable.'
      operationId: 9f398e2652ea47a6dc5121ce5184222a
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                title: CompensationToolsResponse
                properties:
                  success: { type: boolean }
                  tools: { $ref: '#/components/schemas/CompensationToolsDataObject' }
                  upsell: { $ref: '#/components/schemas/CompensationUpsellData' }
                type: object
        '403':
          description: Forbidden
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - compensation_tools
  '/api/v1/employee-verifications/employees/{employeeId}':
    get:
      tags:
        - 'Employee Verification'
        - 'Public API'
      summary: 'List employee verification records for an employee'
      description: 'Returns every employee verification row for the given employee (including archived), newest first. Intended for admin-level API consumers with the employee verifications OAuth scope; callers must have onboarding access and permission to view the employee (same rules as the active-verification employee verification API), except when the OAuth context is the employee themselves.'
      operationId: list-employee-verifications-by-employee
      parameters:
        -
          name: employeeId
          in: path
          description: 'Company employee id whose verification history is returned.'
          required: true
          schema:
            type: integer
            format: int64
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                title: EmployeeVerificationsListResponse
                required:
                  - employeeVerifications
                properties:
                  employeeVerifications: { description: 'Verification records for the employee, newest first.', type: array, items: { $ref: '#/components/schemas/EmployeeVerificationPublicApiRecord' } }
                type: object
                additionalProperties: true
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        -
          oauth:
            - employee_verifications
  '/api/v1/employee-verifications/users/{userId}/send-email':
    post:
      tags:
        - 'Employee Verification'
        - 'Public API'
      summary: 'Send employee verification lifecycle email by user and email type'
      description: 'Accepts a Bamboo `userId` (path) and an `emailType` (body) and queues the corresponding employee verification lifecycle email—the same templates as the in-app auth listener (`signup_survey` after signup, `app_installed` after the integration app is installed). Delivered to the company owner contact with template context for that user id. Not an employee I-9 reminder. Requires the employee verification integration enabled, onboarding edit access, and `employee_verifications.write`.'
      operationId: send-employee-verification-lifecycle-email-by-user
      parameters:
        -
          name: userId
          in: path
          description: 'Bamboo user id for the requesting user in the email template (same role as the listener’s event user id). Together with body `emailType`, selects which verification email to send.'
          required: true
          schema:
            type: integer
            format: int64
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - emailType
              properties:
                emailType:
                  description: 'Which employee verification lifecycle email to send; must be {@see EverifyEmailType::SIGNUP_SURVEY} or {@see EverifyEmailType::APP_INSTALLED} (string values `signup_survey`, `app_installed`).'
                  type: string
                  enum: [signup_survey, app_installed]
              type: object
      responses:
        '202':
          description: 'Request accepted; email has been queued for delivery.'
          content:
            application/json:
              schema:
                title: EmployeeVerificationLifecycleEmailAcceptedResponse
                required:
                  - status
                properties:
                  status: { type: string, example: accepted }
                  message: { type: string, example: 'Employee verification email has been queued for delivery.' }
                type: object
                additionalProperties: true
        '400':
          description: 'Bad request (invalid body, unknown user id, or integration disabled)'
        '403':
          description: Forbidden
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - employee_verifications.write
  '/api/v1/employee-verifications/employees/{employeeId}/{verificationId}':
    put:
      tags:
        - 'Employee Verification'
        - 'Public API'
      summary: 'Update an employee verification record'
      description: 'Partial update of a single employee verification row. Any field omitted from the request body is left unchanged. Returns 404 when the verification id does not exist or does not belong to {employeeId}. Most verification updates flow in via partner webhooks; this endpoint exists for admin-level integrations that need to correct or annotate a record out-of-band, and requires onboarding view-and-edit permission on the employee.'
      operationId: update-employee-verification
      parameters:
        -
          name: employeeId
          in: path
          description: 'Company employee id that owns the verification row.'
          required: true
          schema:
            type: integer
            format: int64
        -
          name: verificationId
          in: path
          description: 'Identifier of the verification record to update.'
          required: true
          schema:
            type: integer
            format: int64
      requestBody:
        description: 'Fields to update on the verification record. At least one field must be supplied.'
        required: true
        content:
          application/json:
            schema:
              properties:
                verificationType:
                  oneOf: [{ type: string, enum: [in-person, hybrid, remote] }, { type: 'null' }]
                verificationStatus:
                  oneOf: [{ type: string, enum: [pending, inprogress-section-1, inprogress-section-2, completed, failed] }, { type: 'null' }]
                verificationStatusNotes:
                  oneOf: [{ type: string }, { type: 'null' }]
                remoteAccessUrl:
                  oneOf: [{ type: string }, { type: 'null' }]
                eVerifyStatus:
                  oneOf: [{ type: string, enum: [pending, inprogress, completed, failed] }, { type: 'null' }]
              type: object
      responses:
        '200':
          description: 'Success — returns the updated verification record.'
          content:
            application/json:
              schema:
                title: EmployeeVerificationUpdateResponse
                required:
                  - employeeVerification
                properties:
                  employeeVerification: { $ref: '#/components/schemas/EmployeeVerificationPublicApiRecord' }
                type: object
        '400':
          description: 'Invalid request body (unknown enum value or no updatable fields supplied).'
        '403':
          description: Forbidden
        '404':
          description: 'Verification id not found for the supplied employee.'
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - employee_verifications.write
  /api/v1/employee-verifications/integration:
    get:
      tags:
        - 'Employee Verification'
        - 'Public API'
      summary: 'Get employee verification integration status'
      description: "Returns the install state and partner metadata for the company's employee verification integration. Scoped to the authenticated OAuth company context. The `enabled` flag reflects whether the partner app is installed and usable; `integrationType` identifies the configured verification partner."
      operationId: get-employee-verification-integration
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                title: EmployeeVerificationIntegrationResponse
                required:
                  - integration
                properties:
                  integration: { $ref: '#/components/schemas/EmployeeVerificationIntegration' }
                type: object
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        -
          oauth:
            - employee_verifications
    put:
      tags:
        - 'Employee Verification'
        - 'Public API'
      summary: 'Enable or disable the employee verification integration'
      description: "Enables or disables the company's employee verification integration. Enabling installs the configured partner app and schedules the daily sync; the company must have a valid US work location before enabling, otherwise a 400 is returned. Disabling tears down the partner app. The call is idempotent: a request to enable when already enabled (or disable when already disabled) returns 200 with the current state. Requires the employee_verifications.write OAuth scope, intended for admin-level API consumers."
      operationId: update-employee-verification-integration
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - enabled
              properties:
                enabled:
                  description: 'Desired enabled state for the integration.'
                  type: boolean
              type: object
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                title: EmployeeVerificationIntegrationResponse
                required:
                  - integration
                properties:
                  integration: { $ref: '#/components/schemas/EmployeeVerificationIntegration' }
                type: object
        '400':
          description: 'Invalid request body or partner configuration prevented the change.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        -
          oauth:
            - employee_verifications.write
  /api/v1/new-hire-packets:
    get:
      tags:
        - Onboarding
        - 'Public API'
      summary: 'List new hire packets'
      description: 'Returns a paginated list of new hire packet instances for the company. Each item is a flat summary (no sections, tasks, questions, or GTKY recipients).'
      operationId: f44b802c30cdea2b9076b3f82f99c74d
      parameters:
        -
          name: page
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
        -
          name: pageSize
          in: query
          required: false
          schema:
            type: integer
            maximum: 100
            minimum: 1
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                title: NewHirePacketsListResponse
                properties:
                  newHirePackets: { description: 'Packet summaries for the current page.', type: array, items: { $ref: '#/components/schemas/NewHirePacketPublicApi' } }
                  page: { description: 'Current page number (1-based).', type: integer }
                  pageSize: { description: 'Page size used for this response.', type: integer }
                  totalCount: { description: 'Total packet rows matching visibility (across all pages).', type: integer }
                type: object
                additionalProperties: true
        '400':
          description: 'Bad request'
        '403':
          description: Forbidden
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - onboarding
    post:
      tags:
        - Onboarding
        - 'Public API'
      summary: 'Create new hire packet'
      description: 'Creates a new hire packet instance (draft) for an employee. Company configuration is applied for default inclusion flags.'
      operationId: ec1ba8e76f33960b018d0d7518fe97b5
      requestBody:
        required: true
        content:
          application/json:
            schema:
              allOf:
                -
                  $ref: '#/components/schemas/NewHirePacketPublicApiWritableBody'
                -
                  required: [employeeId]
                  properties: { employeeId: { description: 'Employee to attach the new packet to.', type: integer } }
                  type: object
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NewHirePacketPublicApi'
        '400':
          description: 'Bad request'
        '403':
          description: Forbidden
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - onboarding.write
  '/api/v1/new-hire-packets/{id}':
    get:
      tags:
        - Onboarding
        - 'Public API'
      summary: 'Get new hire packet by id'
      description: 'Returns a single new hire packet instance with a derived status. Sub-resources (sections, tasks, questions, GTKY) are not included.'
      operationId: 696f0a229cdde60b733568e3c4d043d9
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NewHirePacketPublicApi'
        '403':
          description: Forbidden
        '404':
          description: 'Not found'
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - onboarding
    put:
      tags:
        - Onboarding
        - 'Public API'
      summary: 'Update new hire packet'
      description: 'Updates an existing new hire packet instance. Sent, viewed, and completed timestamps are not changed through this endpoint.'
      operationId: 1ab0279d46023eb951a434f24df885f1
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NewHirePacketPublicApiWritableBody'
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NewHirePacketPublicApi'
        '400':
          description: 'Bad request'
        '403':
          description: Forbidden
        '404':
          description: 'Not found'
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - onboarding.write
    delete:
      tags:
        - Onboarding
        - 'Public API'
      summary: 'Delete new hire packet'
      description: 'Deletes a new hire packet instance by primary key.'
      operationId: caa7fc488bcfaef14125398f2ebb987d
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '204':
          description: 'No content'
        '403':
          description: Forbidden
        '404':
          description: 'Not found'
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - onboarding.write
  '/api/v1/new-hire-packets/{id}/question-visibility':
    put:
      tags:
        - Onboarding
        - 'Public API'
      summary: 'Update GTKY answer visibility for a new hire packet'
      description: 'Shows or hides get-to-know-you (GTKY) question answers for the packet. Omit questionIds to toggle all answers (same as the legacy BFF POST /onboarding/questions/visibility/{newHirePacketId} with a visible query flag). Send questionIds to toggle only specific personal questions linked to this new hire packet. Requires the same edit access as other NHP writes. When the employee has a hire date set, updates are rejected once the company-local calendar date is on or after that date (same rule as GET /onboarding/questions/{newHirePacketId}, which redirects to expired in that case). Draft employees without a hire date are not blocked.'
      operationId: update-new-hire-packet-gtky-answer-visibility
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              title: NewHirePacketGtkyAnswerVisibilityRequest
              required:
                - hidden
              properties:
                hidden:
                  description: 'When true, answers are hidden from typical manager views; when false, answers are shown.'
                  type: boolean
                questionIds:
                  description: 'Optional. When omitted or null, hidden applies to every GTKY answer row for the packet. When a non-empty array of employee personal question ids, only those questions (must belong to this packet) are updated.'
                  oneOf: [{ type: array, items: { type: integer, format: int64 } }, { type: 'null' }]
              type: object
      responses:
        '200':
          description: 'Success — returns the current visibility for each GTKY answer row on the packet.'
          content:
            application/json:
              schema:
                title: NewHirePacketGtkyAnswerVisibilityResponse
                required:
                  - gtkys
                properties:
                  gtkys: { description: 'GTKY personal-question answer rows with visibility flags.', type: array, items: { title: NewHirePacketGtkyAnswerVisibilityItem, required: [responseId, questionId, hidden], properties: { responseId: { type: integer, format: int64 }, questionId: { type: integer, format: int64 }, hidden: { type: boolean } }, type: object } }
                type: object
        '400':
          description: 'Bad request — invalid body (e.g. hidden type, questionIds shape, ids not linked to this packet), or visibility change not allowed on or after the employee hire date when a hire date is set'
        '403':
          description: Forbidden
        '404':
          description: 'Not found'
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - onboarding.write
  '/api/v1/new-hire-packets/{id}/send':
    post:
      tags:
        - Onboarding
        - 'Public API'
      summary: 'Send new hire packet'
      description: "Sends the new hire packet for that packet's employee (email or onboarding experience workflow, depending on company configuration). The packet must be in draft state."
      operationId: f49b0f1f2fb1ef2c408ba12916ee9baa
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NewHirePacketPublicApi'
        '400':
          description: 'Bad request'
        '403':
          description: Forbidden
        '404':
          description: 'Not found'
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - onboarding.write
  '/api/v1/new-hire-packets/{id}/cancel':
    post:
      tags:
        - Onboarding
        - 'Public API'
      summary: 'Cancel new hire packet'
      description: 'Cancels the new hire packet. Completed packets cannot be cancelled.'
      operationId: 19c7e26a1347ae7eb22919e9b0595c19
      parameters:
        -
          name: id
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NewHirePacketPublicApi'
        '400':
          description: 'Bad request'
        '403':
          description: Forbidden
        '404':
          description: 'Not found'
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - onboarding.write
  /api/v1/onboarding/new-hire-widget:
    get:
      tags:
        - Onboarding
        - 'Public API'
      summary: 'Get welcome new hires widget'
      description: 'Returns the upcoming-new-hires data that powers the BambooHR home "Welcome New Hires" widget. Items are ordered by most recent hire date first. The list reflects the authenticated user''s new-hire-packet and company-directory access — when either is missing the response is an empty list (not 403). Sensitive contact fields (work email, home email) are never returned.'
      operationId: 044949386f2d655c6a627ef53f9434b7
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                title: NewHireWidgetResponse
                required:
                  - newHires
                properties:
                  newHires: { description: 'Upcoming new hires visible to the authenticated user, sorted by hire date descending. Empty when the user lacks new-hire-packet or company-directory access.', type: array, items: { $ref: '#/components/schemas/NewHireWidgetItem' } }
                type: object
        '403':
          description: Forbidden
        '500':
          description: 'Internal server error'
      security:
        -
          oauth:
            - onboarding
  '/api/v1/employees/{employeeId}/onboarding-experiences':
    get:
      tags:
        - Onboarding
        - 'Public API'
      summary: 'List employee onboarding experiences'
      description: 'Returns onboarding experiences for the employee when the onboarding experience phase-1 and workflow framework company toggles are enabled, and at least one OnboardingExperienceWorkflow execution is running for them. Each item is projected from the employee’s new hire packet instance row for the same public id contract (no embedded NHP payload).'
      operationId: 0158de7cde2a4c4cf577f0b25070d809
      parameters:
        -
          name: employeeId
          in: path
          required: true
          schema:
            type: integer
            format: int64
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                title: OnboardingExperiencesListResponse
                properties:
                  onboardingExperiences: { description: 'Onboarding experience summaries (empty when no running workflow for this employee).', type: array, items: { $ref: '#/components/schemas/OnboardingExperiencePublicApi' } }
                type: object
                additionalProperties: true
        '403':
          description: 'Forbidden — missing OAuth permission, onboarding experience phase-1 / workflow framework feature not enabled for the company, or caller cannot access the employee.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        -
          oauth:
            - onboarding
    post:
      tags:
        - Onboarding
        - 'Public API'
      summary: 'Create employee onboarding experience'
      description: 'Creates (starts) an onboarding experience workflow for the employee’s existing new hire packet instance. Requires the onboarding experience phase-1 and workflow framework company toggles. Optional sentDateTime may be supplied for workflow idempotency (ISO 8601 or legacy datetime string accepted when non-empty).'
      operationId: 288aa996aba16d7a495c62321ea999a9
      parameters:
        -
          name: employeeId
          in: path
          required: true
          schema:
            type: integer
            format: int64
      requestBody:
        required: false
        content:
          application/json:
            schema:
              properties:
                sentDateTime:
                  description: 'Optional sent datetime for the packet when starting the experience (string).'
                  oneOf: [{ type: string }, { type: 'null' }]
              type: object
              additionalProperties: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OnboardingExperiencePublicApi'
        '400':
          description: 'Bad request'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: 'Forbidden — missing OAuth permission, or onboarding experience phase-1 / workflow framework feature not enabled for the company.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: 'Conflict — an onboarding experience workflow already exists for this employee.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: 'Workflow instance could not be started or is not running.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        -
          oauth:
            - onboarding.write
  '/api/v1/employees/{employeeId}/onboarding-experiences/{onboardingExperienceId}':
    get:
      tags:
        - Onboarding
        - 'Public API'
      summary: 'Get employee onboarding experience by id'
      description: 'Returns a single onboarding experience when the onboarding experience phase-1 and workflow framework company toggles are enabled, the employee has at least one running OnboardingExperienceWorkflow (execution status RUNNING), the path id matches their current new hire packet instance id, and the row belongs to the employee. NHP fields are referenced by id only.'
      operationId: 847dd061d1d1859e7ce8cb3adfc9faf2
      parameters:
        -
          name: employeeId
          in: path
          required: true
          schema:
            type: integer
            format: int64
        -
          name: onboardingExperienceId
          in: path
          required: true
          schema:
            type: integer
            format: int64
      responses:
        '200':
          description: Success
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OnboardingExperiencePublicApi'
        '403':
          description: 'Forbidden — missing OAuth permission, onboarding experience phase-1 / workflow framework feature not enabled for the company, or caller cannot access the employee.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: 'Not found — no matching running workflow, id does not match the employee’s current packet instance, or no such row.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        -
          oauth:
            - onboarding
  /api/v1/time_tracking/projects:
    post:
      tags:
        - 'Time Tracking'
        - 'Public API'
      summary: 'Create Time Tracking Project'
      description: "Creates a new time tracking project. Returns the created project with its ID and tasks. When `allowAllEmployees` is false, the response also includes the `employeeIds` array of employees who have access.\n\nBy default, all employees can log time to the project (`allowAllEmployees` defaults to true) and the project is billable (`billable` defaults to true). Set `hasTasks` to true to enable task-level tracking and provide a `tasks` array — at least one task is required when `hasTasks` is true. Project and task names must be unique and may not exceed 50 characters. When `allowAllEmployees` is false, provide `employeeIds` to restrict access to specific employees."
      operationId: create-time-tracking-project
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProjectCreateRequestSchema'
      responses:
        '201':
          description: 'Project created successfully. Returns the new project with tasks. The `employeeIds` field is included only when `allowAllEmployees` is false.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimeTrackingProjectWithTasksAndEmployeeIds'
        '400':
          description: 'Bad request parameters - invalid or missing required fields.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden. Insufficient user permissions or API access is not turned on.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '406':
          description: 'Request not acceptable.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '409':
          description: 'There was a conflict with the request.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - time_tracking.write
  /api/v1/scheduling/schedules:
    get:
      tags:
        - Scheduling
        - 'Public API'
      summary: 'List Schedules'
      description: 'Retrieves a paginated list of all schedules for the company. Supports optional OData-style filtering and sorting.'
      operationId: scheduling-list-schedules
      parameters:
        -
          name: filter
          in: query
          description: "OData filter expression applied to schedules. Supported operators: `eq` (equals, use `eq null` to match NULL), `ne` (not equals, use `ne null` to match NOT NULL), `lt` (less than), `le` (less than or equal), `gt` (greater than), `ge` (greater than or equal), `in` (value in list), `and` (combine clauses). Not supported: `or`, `not`, parenthesized grouping. Filterable fields: `id`, `name`, `locationId`, `timezone`, `startOfWeek`, `earlyClockInThreshold`, `createdAt`, `updatedAt`, `deletedAt`. Examples: `name eq 'Default Schedule'`, `deletedAt eq null`, `name eq 'Default Schedule' and locationId in (1, 2)`."
          required: false
          schema:
            type: string
        -
          name: sort
          in: query
          description: 'Sorting options. Supported sorts are: name, locationId, timezone, startOfWeek, earlyClockInThreshold, createdAt, updatedAt, deletedAt'
          required: false
          schema:
            type: string
        -
          name: page
          in: query
          description: 'The starting page for pagination'
          required: false
          schema:
            type: integer
            default: 1
        -
          name: pageSize
          in: query
          description: 'The number of items to be returned'
          required: false
          schema:
            type: integer
            default: 100
      responses:
        '200':
          description: 'A list of schedules'
          content:
            application/json:
              schema:
                title: Scheduling-ScheduleListResponse-V1
                properties:
                  data: { description: 'Array of schedule resource objects', type: array, items: { $ref: '#/components/schemas/Scheduling-Schedule-V1' } }
                  meta: { properties: { totalItems: { description: 'Total number of schedules matching the filter.', type: integer }, page: { description: 'Current page number.', type: integer }, pageSize: { description: 'Number of items per page.', type: integer } }, type: object }
                  _links: { properties: { prev: { description: 'Link to the previous page, or null if on the first page.', properties: { href: { description: 'URL for the previous page.', type: string } }, oneOf: [{ type: object }, { type: 'null' }] }, next: { description: 'Link to the next page, or null if on the last page.', properties: { href: { description: 'URL for the next page.', type: string } }, oneOf: [{ type: object }, { type: 'null' }] } }, type: object }
                type: object
        '400':
          description: 'Bad request - invalid filter, sort, or pagination parameters.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden - insufficient permissions.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Unprocessable entity - invalid filter or sort syntax.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'scheduling:schedules'
        -
          oauth:
            - time_tracking
    post:
      tags:
        - Scheduling
        - 'Public API'
      summary: 'Create Schedule'
      description: 'Creates a new schedule for the company. Rejects duplicate schedule data for the same location and configuration.'
      operationId: scheduling-create-schedule
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Scheduling-CreateScheduleRequest-V1'
      responses:
        '201':
          description: 'Schedule created successfully'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Scheduling-Schedule-V1'
        '403':
          description: 'Forbidden - insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Unprocessable entity - validation failed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'scheduling:schedules.write'
        -
          oauth:
            - time_tracking.write
  '/api/v1/scheduling/schedules/{id}':
    get:
      tags:
        - Scheduling
        - 'Public API'
      summary: 'Get Schedule'
      description: 'Retrieves a specific schedule by its UUID. Schedules organize shifts for employees.'
      operationId: scheduling-get-schedule
      parameters:
        -
          name: id
          in: path
          description: 'The ID of the schedule'
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: 'Schedule retrieved successfully'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Scheduling-Schedule-V1'
        '403':
          description: 'Forbidden - insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Schedule not found.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Unprocessable entity - invalid UUID format.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'scheduling:schedules'
        -
          oauth:
            - time_tracking
    delete:
      tags:
        - Scheduling
        - 'Public API'
      summary: 'Delete Schedule'
      description: 'Deletes a schedule by its UUID. Unworked shifts will be deleted as a result.'
      operationId: scheduling-delete-schedule
      parameters:
        -
          name: id
          in: path
          description: 'The schedule UUID'
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '204':
          description: 'Schedule deleted successfully'
        '403':
          description: 'Forbidden - insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Schedule not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Invalid request data'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'scheduling:schedules.write'
        -
          oauth:
            - time_tracking.write
    patch:
      tags:
        - Scheduling
        - 'Public API'
      summary: 'Update Schedule'
      description: 'Partially updates an existing schedule by its UUID. Fields not provided retain their existing values.'
      operationId: scheduling-update-schedule
      parameters:
        -
          name: id
          in: path
          description: 'The schedule UUID'
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Scheduling-UpdateScheduleRequest-V1'
      responses:
        '200':
          description: 'Schedule updated successfully'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Scheduling-Schedule-V1'
        '403':
          description: 'Forbidden - insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Schedule not found.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Unprocessable entity - validation failed.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'scheduling:schedules.write'
        -
          oauth:
            - time_tracking.write
  '/api/v1/scheduling/schedules/{id}/pdf':
    get:
      tags:
        - Scheduling
        - 'Public API'
      summary: 'Get Schedule PDF'
      description: 'Generates and streams a PDF of the schedule view for the given schedule and date range.'
      operationId: scheduling-get-schedule-pdf
      parameters:
        -
          name: id
          in: path
          description: 'The schedule ID'
          required: true
          schema:
            type: string
        -
          name: startYmd
          in: query
          description: 'Start date in YYYY-MM-DD format'
          required: true
          schema:
            type: string
            format: date
        -
          name: endYmd
          in: query
          description: 'End date in YYYY-MM-DD format'
          required: true
          schema:
            type: string
            format: date
        -
          name: groupBy
          in: query
          description: 'Group employees by this field'
          required: false
          schema:
            type: string
            enum:
              - location
              - department
              - division
              - jobTitle
              - team
        -
          name: 'employeeIds[]'
          in: query
          description: 'Filter by employee IDs. Pass the string "null" as an element to include open shifts.'
          required: false
          schema:
            type: array
            items:
              type: string
        -
          name: includeEmployeesWithoutShifts
          in: query
          description: 'Whether to include employees who have no shifts in the date range'
          required: false
          schema:
            type: boolean
            default: false
        -
          name: includeHolidays
          in: query
          description: 'Whether to include holidays in the PDF'
          required: false
          schema:
            type: boolean
            default: false
        -
          name: includeTimeOff
          in: query
          description: 'Whether to include time-off entries in the PDF'
          required: false
          schema:
            type: boolean
            default: false
      responses:
        '200':
          description: 'PDF binary stream'
          content:
            application/pdf: {  }
        '403':
          description: Forbidden
        '404':
          description: 'Schedule not found'
        '422':
          description: 'Invalid parameters'
        '500':
          description: 'Failed to render PDF'
      security:
        -
          oauth:
            - 'scheduling:schedules'
  /api/v1/scheduling/timezones:
    get:
      tags:
        - Scheduling
        - 'Public API'
      summary: 'List Timezones'
      description: 'Returns a paginated list of timezones available for use when creating or editing schedules. Supports OData-style filtering on the `name` field.'
      operationId: scheduling-list-timezones
      parameters:
        -
          name: filter
          in: query
          description: "OData filter expression applied to the `name` field. Supported operators: `eq` (exact match), `ne` (exclude), `and` (combine clauses). Supported string functions: `contains`, `startswith`, `endswith` (all case-insensitive). Not supported: `or`, `not`, parenthesized grouping. Examples: `name eq 'America/Denver'`, `name ne 'UTC'`, `contains(name, 'America')`, `startswith(name, 'US/')`, `endswith(name, 'York')`."
          required: false
          schema:
            type: string
        -
          name: sort
          in: query
          description: 'Sorting options. Supported sort fields: `name` (alphabetical), `offset` (numeric UTC offset). Example: `name asc` or `offset desc`'
          required: false
          schema:
            type: string
        -
          name: page
          in: query
          description: 'The starting page for pagination. Defaults to 1.'
          required: false
          schema:
            type: integer
            default: 1
        -
          name: pageSize
          in: query
          description: 'The number of items to be returned. Defaults to 100.'
          required: false
          schema:
            type: integer
            default: 100
      responses:
        '200':
          description: 'A paginated list of timezones.'
          content:
            application/json:
              schema:
                title: Scheduling-TimezoneListResponse-V1
                properties:
                  data: { type: array, items: { $ref: '#/components/schemas/Scheduling-Timezone-V1' } }
                  meta: { properties: { totalItems: { description: 'Total number of timezones matching the filter.', type: integer }, page: { description: 'Current page number.', type: integer }, pageSize: { description: 'Number of items per page.', type: integer } }, type: object }
                  _links: { properties: { prev: { description: 'Link to the previous page, or null if on the first page.', properties: { href: { type: string } }, oneOf: [{ type: object }, { type: 'null' }] }, next: { description: 'Link to the next page, or null if on the last page.', properties: { href: { type: string } }, oneOf: [{ type: object }, { type: 'null' }] } }, type: object }
                type: object
        '403':
          description: 'Forbidden - insufficient permissions.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Unprocessable entity - invalid filter or pagination parameters.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'scheduling:schedules'
        -
          oauth:
            - time_tracking
  '/api/v1/scheduling/shifts/{id}':
    get:
      tags:
        - Scheduling
        - 'Public API'
      summary: 'Get Shift'
      description: 'Retrieves a single shift by its UUID. Returns the full shift object including recurrence settings, assigned employee IDs, and status. Returns 404 if the shift does not exist or has been deleted.'
      operationId: scheduling-get-shift
      parameters:
        -
          name: id
          in: path
          description: 'The shift uuid'
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: 'Successfully fetched a shift'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Scheduling-SchedulingShift-V1'
        '403':
          description: 'Forbidden - user does not have permission'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Not Found - shift not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Invalid request data'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'scheduling:shifts'
        -
          oauth:
            - time_tracking
    delete:
      tags:
        - Scheduling
        - 'Public API'
      summary: 'Delete Shift'
      description: 'Deletes a shift by its UUID or composite recurrence ID. Use `recurrenceEditOption` to control the scope to delete one or many consecutive recurring shifts'
      operationId: scheduling-delete-shift
      parameters:
        -
          name: id
          in: path
          description: 'The shift UUID, or the projected shift composite ID.'
          required: true
          schema:
            type: string
            format: uuid
        -
          name: recurrenceEditOption
          in: query
          description: "How should the shift be deleted? Choose 'instance' for deleting a single target shift, \n\t\t'future' for deleting the target shift and all following shifts that are in the same recurrence, \n\t\t'all' for deleting all shifts starting from now that are in the same recurrence as the target shift."
          required: false
          schema:
            type: string
            default: instance
            enum:
              - instance
              - future
              - all
            example: future
      responses:
        '204':
          description: 'Shift deleted successfully. No content returned.'
        '403':
          description: 'Forbidden - user does not have permission.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Not Found - shift not found.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Invalid request data.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'scheduling:shifts.write'
        -
          oauth:
            - time_tracking.write
    patch:
      tags:
        - Scheduling
        - 'Public API'
      summary: 'Update Shift'
      description: 'Updates a shift by its UUID. Fields not provided will retain their existing values. For published shifts, changes are not applied directly — they are stored as pending (`unpublishedChanges`) and take effect when the shift is published. `recurrenceEditOption` is required when the shift already has recurrence settings; ALL and FUTURE edits propagate to published sibling occurrences the same way, staging changes rather than applying them immediately. Sibling shifts may be permanently removed if they no longer conform to changed recurrence fields.'
      operationId: scheduling-update-shift
      parameters:
        -
          name: id
          in: path
          description: 'The shift uuid'
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Scheduling-UpdateSchedulingShiftRequest-V1'
      responses:
        '200':
          description: 'Successfully updated a shift'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Scheduling-SchedulingShift-V1'
        '403':
          description: 'Forbidden - user does not have permission'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Not Found - shift not found.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Invalid request data'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'scheduling:shifts.write'
        -
          oauth:
            - time_tracking.write
  /api/v1/scheduling/shifts:
    get:
      tags:
        - Scheduling
        - 'Public API'
      summary: 'List Shifts'
      description: 'Lists shifts matching the given filters. Either provide `ids` (ignores all other filters) or provide `start`, `end`, and at least one of `employeeIds` or `scheduleIds`. The time window must be no longer than 1 month. Defaults to returning planned and published shifts.'
      operationId: scheduling-list-shifts
      parameters:
        -
          name: ids
          in: query
          description: 'Filter by shift IDs. If provided, other filters are ignored.'
          required: false
          schema:
            type: array
            items:
              type: string
              format: uuid
        -
          name: start
          in: query
          description: 'Start datetime for the shift window (ISO-8601 datetime string). Required if ids not provided.'
          required: false
          schema:
            type: string
            format: date-time
        -
          name: end
          in: query
          description: 'End datetime for the shift window (ISO-8601 datetime string). Required if ids not provided.'
          required: false
          schema:
            type: string
            format: date-time
        -
          name: employeeIds
          in: query
          description: 'Filter by employee IDs. Use the string "null" as a value to include unassigned shifts (e.g. employeeIds[]=10&employeeIds[]=null).'
          required: false
          schema:
            type: array
            items:
              type: string
        -
          name: scheduleIds
          in: query
          description: 'Filter by schedule IDs.'
          required: false
          schema:
            type: array
            items:
              type: string
              format: uuid
        -
          name: statuses
          in: query
          description: 'Filter by shift statuses.'
          required: false
          schema:
            type: array
            items:
              type: string
              enum:
                - planned
                - published
                - cancelled
                - deleted
            default:
              - planned
              - published
        -
          name: page
          in: query
          description: 'The starting page for pagination.'
          required: false
          schema:
            type: integer
            default: 1
            minimum: 1
        -
          name: pageSize
          in: query
          description: 'The number of items to be returned.'
          required: false
          schema:
            type: integer
            default: 1000
            maximum: 1000
            minimum: 1
      responses:
        '200':
          description: 'Shifts retrieved successfully'
          content:
            application/json:
              schema:
                title: Scheduling-ShiftListResponse-V1
                properties:
                  data: { description: 'Array of shifts', type: array, items: { $ref: '#/components/schemas/Scheduling-SchedulingShift-V1' } }
                  meta: { properties: { totalItems: { description: 'Total number of shifts matching the filter.', type: integer }, page: { description: 'Current page number.', type: integer }, pageSize: { description: 'Number of items per page.', type: integer } }, type: object }
                  _links: { properties: { prev: { description: 'Link to the previous page.', properties: { href: { description: 'URL for the previous page, or null if on the first page.', oneOf: [{ type: string }, { type: 'null' }] } }, type: object }, next: { description: 'Link to the next page.', properties: { href: { description: 'URL for the next page, or null if on the last page.', oneOf: [{ type: string }, { type: 'null' }] } }, type: object } }, type: object }
                type: object
        '403':
          description: 'Forbidden - insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Invalid input'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'scheduling:shifts'
        -
          oauth:
            - time_tracking
    post:
      tags:
        - Scheduling
        - 'Public API'
      summary: 'Create Shift'
      description: 'Creates a new shift in the specified schedule.'
      operationId: scheduling-create-shift
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Scheduling-CreateSchedulingShiftRequest-V1'
      responses:
        '201':
          description: 'Successfully created a shift.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Scheduling-SchedulingShift-V1'
        '403':
          description: 'Forbidden - user does not have permission.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Not Found - the referenced schedule does not exist.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Invalid request data.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'scheduling:shifts.write'
        -
          oauth:
            - time_tracking.write
  /api/v1/scheduling/shifts/publish:
    post:
      tags:
        - Scheduling
        - 'Public API'
      summary: 'Publish Shifts'
      description: 'Publishes one or more planned shifts, making them visible to employees. Shifts with scheduling conflicts are skipped and reported as failures. Returns 200 if all shifts succeed, 207 if some shifts failed due to conflicts, or 409 if all shifts failed due to conflicts.'
      operationId: scheduling-publish-shifts
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - shiftIds
              properties:
                shiftIds:
                  description: 'Shift IDs to publish.'
                  type: array
                  items: { type: string }
                  maxItems: 100
                  minItems: 1
              type: object
      responses:
        '200':
          description: 'All shifts published successfully'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Scheduling-PublishShiftsResult-V1'
        '207':
          description: 'Some shifts published successfully, but one or more failed due to conflicts'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Scheduling-PublishShiftsResult-V1'
        '409':
          description: 'All shifts failed to publish due to conflicts'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Scheduling-PublishShiftsResult-V1'
        '403':
          description: 'Forbidden - user does not have permission'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Invalid request data'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'scheduling:shifts.write'
        -
          oauth:
            - time_tracking.write
  /api/v1/time-tracking/break-assessments:
    get:
      tags:
        - 'Meal & Rest Breaks'
        - 'Public API'
      summary: 'List Break Assessments'
      description: 'Returns a paginated list of break assessments. A break assessment records whether an employee complied with their assigned break policy for a given day, along with any violations. Use the `filter` parameter to scope results by employee, date, result, or other fields. Use `offset` and `limit` for pagination; `limit` defaults to 100 and may not exceed 500.'
      operationId: list-break-assessments
      parameters:
        -
          name: offset
          in: query
          description: 'Number of items to skip before returning results. Defaults to 0.'
          required: false
          schema:
            type: integer
            default: 0
            minimum: 0
        -
          name: limit
          in: query
          description: 'Maximum number of items to return. Defaults to 100. Maximum is 500.'
          required: false
          schema:
            type: integer
            default: 100
            maximum: 500
            minimum: 0
        -
          name: filter
          in: query
          description: "OData filter expression applied to break assessments. Supported operators: `eq` (equals, use `eq null` to match NULL), `ne` (not equals, use `ne null` to match NOT NULL), `lt` (less than), `le` (less than or equal), `gt` (greater than), `ge` (greater than or equal), `in` (value in list), `and` (combine clauses). Not supported: `or`, `not`, parenthesized grouping. Filterable fields: `id`, `breakId`, `employeeId`, `employeeTimesheetId`, `date`, `result`, `availableYmdt`, `unavailableYmdt`, `expectedDuration`, `recordedDuration`, `durationDifference`, `createdAt`, `updatedAt`. Examples: `employeeId eq 614`, `employeeId in (614, 615, 616)`, `breakId eq 'abc-123' and employeeId eq 614`."
          required: false
          schema:
            type: string
            default: ''
      responses:
        '200':
          description: 'A list of paginated break assessments'
          content:
            application/json:
              schema:
                title: TimeTracking-PaginatedBreakAssessmentsResponse-V1
                type: object
                allOf:
                  - { $ref: '#/components/schemas/TimeTracking-OffsetPaginatedResponseData-V1' }
                  - { properties: { data: { $ref: '#/components/schemas/TimeTracking-TimeTrackingBreakAssessmentCollection-V1', description: 'The break assessments' } }, type: object }
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Invalid input'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:breaks'
  '/api/v1/time-tracking/breaks/{id}':
    get:
      tags:
        - 'Meal & Rest Breaks'
        - 'Public API'
      summary: 'Get Break'
      description: 'Retrieves a single time tracking break by its UUID. Returns the full break details including name, duration, paid status, and availability configuration.'
      operationId: get-break
      parameters:
        -
          name: id
          in: path
          description: 'The break ID.'
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: 'Successfully retrieved the break.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimeTracking-TimeTrackingBreak-V1'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Break not found.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'The provided `id` is not a valid UUID.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:breaks'
    delete:
      tags:
        - 'Meal & Rest Breaks'
        - 'Public API'
      summary: 'Delete Break'
      description: 'Deletes a time tracking break by its UUID. The break is soft-deleted and removed from any break policies it was associated with.'
      operationId: delete-break
      parameters:
        -
          name: id
          in: path
          description: 'The break ID.'
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '204':
          description: 'Break deleted successfully'
        '400':
          description: 'Invalid ID format'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Break not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:breaks.write'
    patch:
      tags:
        - 'Meal & Rest Breaks'
        - 'Public API'
      summary: 'Update Break'
      description: 'Partially updates a time tracking break identified by its UUID. Only fields provided in the request body are updated. Returns the updated break on success.'
      operationId: update-break
      parameters:
        -
          name: id
          in: path
          description: 'The break ID.'
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TimeTracking-UpdateTimeTrackingBreak-V1'
      responses:
        '200':
          description: 'Successfully updated break'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimeTracking-TimeTrackingBreak-V1'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Invalid request data'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:breaks.write'
  '/api/v1/time-tracking/break-policies/{id}/breaks':
    get:
      tags:
        - 'Meal & Rest Breaks'
        - 'Public API'
      summary: 'List Breaks for Break Policy'
      description: 'Returns a paginated list of breaks belonging to the specified break policy. Supports OData v4 filtering.'
      operationId: list-break-policy-breaks
      parameters:
        -
          name: id
          in: path
          description: 'The break policy ID.'
          required: true
          schema:
            type: string
            format: uuid
        -
          name: offset
          in: query
          description: 'The offset of items to retrieve'
          schema:
            type: integer
            default: 0
        -
          name: limit
          in: query
          description: 'The maximum items to retrieve'
          schema:
            type: integer
            default: 100
            maximum: 500
        -
          name: filter
          in: query
          description: "OData filter expression applied to breaks within the policy. Supported operators: `eq` (equals, use `eq null` to match NULL), `ne` (not equals, use `ne null` to match NOT NULL), `lt` (less than), `le` (less than or equal), `gt` (greater than), `ge` (greater than or equal), `in` (value in list), `and` (combine clauses). Not supported: `or`, `not`, parenthesized grouping. Filterable fields: `id`, `name`, `paid`, `duration`, `availabilityType`, `availabilityMinHoursWorked`, `availabilityMaxHoursWorked`, `availabilityStartTime`, `availabilityEndTime`, `createdAt`, `updatedAt`, `deletedAt`. Examples: `name eq 'Lunch'`, `paid eq true`, `paid eq false and name ne 'Quick Break'`."
          schema:
            type: string
            default: ''
      responses:
        '200':
          description: 'Successfully retrieved breaks for the specified break policy'
          content:
            application/json:
              schema:
                title: TimeTracking-PaginatedBreaksResponse-V1
                type: object
                allOf:
                  - { $ref: '#/components/schemas/TimeTracking-OffsetPaginatedResponseData-V1' }
                  - { properties: { data: { $ref: '#/components/schemas/TimeTracking-TimeTrackingBreakCollection-V1', description: 'The breaks' } }, type: object }
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Invalid input'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:breaks'
    put:
      tags:
        - 'Meal & Rest Breaks'
        - 'Public API'
      summary: 'Replace Breaks for Break Policy'
      description: 'Replace all breaks for a break policy. Breaks with an ID will be updated, breaks without an ID will be created. Existing breaks not in the request will be soft-deleted.'
      operationId: replace-breaks-for-break-policy
      parameters:
        -
          name: id
          in: path
          description: 'The break policy ID.'
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TimeTracking-CreateOrUpdateTimeTrackingBreakWithoutPolicyCollection-V1'
      responses:
        '200':
          description: 'Successfully replaced breaks for the policy'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimeTracking-TimeTrackingBreakCollection-V1'
        '400':
          description: 'Bad request - validation errors'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Break policy not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:breaks.write'
    post:
      tags:
        - 'Meal & Rest Breaks'
        - 'Public API'
      summary: 'Create Break'
      description: 'Creates a new break and associates it with the specified break policy.'
      operationId: create-break
      parameters:
        -
          name: id
          in: path
          description: 'The break policy ID.'
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TimeTracking-CreateTimeTrackingBreak-V1'
      responses:
        '201':
          description: 'Successfully created a break'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimeTracking-TimeTrackingBreak-V1'
        '422':
          description: 'Invalid request data'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden - user does not have permission'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:breaks.write'
  '/api/v1/time-tracking/break-policies/{id}/assign':
    put:
      tags:
        - 'Meal & Rest Breaks'
        - 'Public API'
      summary: 'Set Employees for Break Policy'
      description: 'Sets the employee assignments for a break policy. This replaces all existing assignments with the provided list.'
      operationId: set-break-policy-employees
      parameters:
        -
          name: id
          in: path
          description: 'The break policy ID.'
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - employeeIds
              properties:
                employeeIds:
                  description: 'Array of employee IDs to assign to the break policy. Replaces all existing assignments.'
                  type: array
                  items: { type: integer }
              type: object
      responses:
        '204':
          description: 'Employees assigned successfully'
        '400':
          description: 'Invalid data provided.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Break policy not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Unprocessable entity. The provided `id` is not a valid UUID, or `employeeIds` is not an array of positive integers.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:breaks.write'
    post:
      tags:
        - 'Meal & Rest Breaks'
        - 'Public API'
      summary: 'Assign Employees to Break Policy'
      description: 'Assigns employees to a break policy. Adds the specified employees to the policy without removing existing assignments.'
      operationId: assign-employees-to-break-policy
      parameters:
        -
          name: id
          in: path
          description: 'The break policy ID.'
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - employeeIds
              properties:
                employeeIds:
                  description: 'Array of employee IDs to assign to the break policy'
                  type: array
                  items: { type: integer }
                  minItems: 1
              type: object
      responses:
        '204':
          description: 'Employees successfully assigned to the break policy'
        '400':
          description: 'Bad request - invalid data provided (for example: missing `employeeIds`, invalid IDs, or policy assignment constraints).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden - insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Break policy not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Unprocessable entity - validation failed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:breaks.write'
  '/api/v1/time-tracking/break-policies/{id}/unassign':
    post:
      tags:
        - 'Meal & Rest Breaks'
        - 'Public API'
      summary: 'Unassign Employees from Break Policy'
      description: 'Unassigns the specified employees from a break policy. Removes employee assignments from the policy without affecting the policy itself or other assigned employees. Employees can only be unassigned from policies that are not assigned to all employees.'
      operationId: unassign-employees-from-break-policy
      parameters:
        -
          name: id
          in: path
          description: 'The break policy ID.'
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - employeeIds
              properties:
                employeeIds:
                  description: 'Array of employee IDs to unassign from the break policy'
                  type: array
                  items: { type: integer }
                  minItems: 1
              type: object
      responses:
        '204':
          description: 'Employees successfully unassigned from break policy'
        '400':
          description: 'Bad request - Invalid data or policy is assigned to all employees'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Break policy not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Unprocessable entity - validation failed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:breaks.write'
  '/api/v1/time-tracking/break-policies/{id}':
    get:
      tags:
        - 'Meal & Rest Breaks'
        - 'Public API'
      summary: 'Get Break Policy'
      description: 'Retrieves a single break policy by its UUID. When includeCounts is enabled, the response includes the number of associated employees and breaks.'
      operationId: get-break-policy
      parameters:
        -
          name: id
          in: path
          description: 'The break policy ID.'
          required: true
          schema:
            type: string
            format: uuid
        -
          name: includeCounts
          in: query
          description: 'Include employee and break counts'
          required: false
          schema:
            type: boolean
            default: false
      responses:
        '200':
          description: 'Break policy retrieved successfully'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimeTracking-TimeTrackingBreakPolicy-V1'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Break policy not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Invalid input'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:breaks'
    delete:
      tags:
        - 'Meal & Rest Breaks'
        - 'Public API'
      summary: 'Delete Break Policy'
      description: 'Deletes a break policy by its UUID. Associated breaks and employee assignments are also removed.'
      operationId: delete-break-policy
      parameters:
        -
          name: id
          in: path
          description: 'The break policy ID.'
          required: true
          schema:
            type: string
            format: uuid
      responses:
        '204':
          description: 'Break policy deleted successfully'
        '400':
          description: 'Invalid uuid format'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Break policy not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:breaks.write'
    patch:
      tags:
        - 'Meal & Rest Breaks'
        - 'Public API'
      summary: 'Update Break Policy'
      description: 'Partially updates a break policy identified by its UUID. Only fields provided in the request body are updated. Returns the updated break policy on success.'
      operationId: update-break-policy
      parameters:
        -
          name: id
          in: path
          description: 'The break policy ID.'
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TimeTracking-UpdateTimeTrackingBreakPolicy-V1'
      responses:
        '200':
          description: 'Break policy updated successfully'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimeTracking-TimeTrackingBreakPolicy-V1'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Break policy not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Invalid uuid format'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:breaks.write'
  /api/v1/time-tracking/break-policies:
    get:
      tags:
        - 'Meal & Rest Breaks'
        - 'Public API'
      summary: 'List Break Policies'
      description: 'Returns a paginated list of all break policies. Supports OData v4 filtering. Use includeCounts to include employee and break counts per policy.'
      operationId: list-break-policies
      parameters:
        -
          name: offset
          in: query
          description: 'The offset of items to retrieve'
          schema:
            type: integer
            default: 0
        -
          name: limit
          in: query
          description: 'The maximum items to retrieve'
          schema:
            type: integer
            default: 100
            maximum: 500
        -
          name: filter
          in: query
          description: "OData filter expression applied to break policies. Supported operators: `eq` (equals, use `eq null` to match NULL), `ne` (not equals, use `ne null` to match NOT NULL), `lt` (less than), `le` (less than or equal), `gt` (greater than), `ge` (greater than or equal), `in` (value in list), `and` (combine clauses). Not supported: `or`, `not`, parenthesized grouping. Filterable fields: `id`, `name`, `description`, `allEmployeesAssigned`, `createdAt`, `updatedAt`, `deletedAt`. Examples: `name eq 'Standard Lunch'`, `description eq null`, `allEmployeesAssigned eq true and name ne 'Legacy'`."
          schema:
            type: string
            default: ''
        -
          name: includeCounts
          in: query
          description: 'Include employee and break counts'
          required: false
          schema:
            type: boolean
            default: false
      responses:
        '200':
          description: 'Successfully retrieved break policies'
          content:
            application/json:
              schema:
                title: TimeTracking-PaginatedBreakPoliciesResponse-V1
                type: object
                allOf:
                  - { $ref: '#/components/schemas/TimeTracking-OffsetPaginatedResponseData-V1' }
                  - { properties: { data: { $ref: '#/components/schemas/TimeTracking-TimeTrackingBreakPolicyCollection-V1', description: 'The break policies' } }, type: object }
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Invalid input'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:breaks'
    post:
      tags:
        - 'Meal & Rest Breaks'
        - 'Public API'
      summary: 'Create Break Policy'
      description: 'Create a break policy. Breaks and assignments can be optionally included and created at the same time.'
      operationId: create-break-policy
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TimeTracking-CreateTimeTrackingBreakPolicy-V1'
      responses:
        '201':
          description: 'Successfully created a break policy'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimeTracking-TimeTrackingBreakPolicyWithRelations-V1'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Invalid request data'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:breaks.write'
  '/api/v1/time-tracking/employees/{id}/break-availabilities':
    get:
      tags:
        - 'Meal & Rest Breaks'
        - 'Public API'
      summary: 'List Employee Break Availabilities'
      description: 'Retrieves break availability information for an employee. Requires permission to view the target employee in addition to time-tracking-break access.'
      operationId: list-employee-break-availabilities
      parameters:
        -
          name: id
          in: path
          description: 'The employee ID.'
          required: true
          schema:
            type: integer
        -
          name: effective
          in: query
          description: 'The employee''s local time that should be used to calculate availability. Defaults to the current time. Must be in Y-m-d\TH:i:s format (no timezone offset).'
          required: false
          schema:
            type: string
            pattern: '^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$'
          example: '2025-12-15T14:30:00'
      responses:
        '200':
          description: 'Successfully retrieved break availabilities for the specified employee'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimeTracking-TimeTrackingBreakAvailabilityCollection-V1'
        '403':
          description: 'Forbidden - insufficient permissions to view this employee or break settings.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Invalid input'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:breaks'
  '/api/v1/time-tracking/employees/{id}/break-policies':
    get:
      tags:
        - 'Meal & Rest Breaks'
        - 'Public API'
      summary: 'List Employee Break Policies'
      description: 'Retrieves break policies assigned to a specific employee. Requires permission to view the target employee.'
      operationId: list-employee-break-policies
      parameters:
        -
          name: id
          in: path
          description: 'The employee ID.'
          required: true
          schema:
            type: integer
        -
          name: offset
          in: query
          description: 'The number of items to skip before starting to collect the result set. Minimum 0. Defaults to 0.'
          required: false
          schema:
            type: integer
            default: 0
            minimum: 0
        -
          name: limit
          in: query
          description: 'The maximum number of items to return. Must be between 0 and 500. Defaults to 100.'
          required: false
          schema:
            type: integer
            default: 100
            maximum: 500
            minimum: 0
      responses:
        '200':
          description: 'A list of break policies'
          content:
            application/json:
              schema:
                title: TimeTracking-PaginatedBreakPoliciesResponse-V1
                type: object
                allOf:
                  - { $ref: '#/components/schemas/TimeTracking-OffsetPaginatedResponseData-V1' }
                  - { properties: { data: { $ref: '#/components/schemas/TimeTracking-TimeTrackingBreakPolicyCollection-V1', description: 'The break policies' } }, type: object }
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Invalid input'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:breaks'
  '/api/v1/time-tracking/break-policies/{id}/employees':
    get:
      tags:
        - 'Meal & Rest Breaks'
        - 'Public API'
      summary: 'List Break Policy Employees'
      description: 'Retrieves employees assigned to a specific break policy. If a policy has no assignments, returns HTTP 200 with an empty `data` array.'
      operationId: list-break-policy-employees
      parameters:
        -
          name: id
          in: path
          description: 'The break policy ID.'
          required: true
          schema:
            type: string
            format: uuid
        -
          name: offset
          in: query
          description: 'The offset of items to retrieve'
          schema:
            type: integer
            default: 0
        -
          name: limit
          in: query
          description: 'The maximum items to retrieve'
          schema:
            type: integer
            default: 100
            maximum: 500
      responses:
        '200':
          description: 'A list of employees assigned to a break policy'
          content:
            application/json:
              schema:
                title: TimeTracking-PaginatedBreakPolicyEmployeesResponse-V1
                type: object
                allOf:
                  - { $ref: '#/components/schemas/TimeTracking-OffsetPaginatedResponseData-V1' }
                  - { properties: { data: { $ref: '#/components/schemas/TimeTracking-BreakPolicyEmployeeCollection-V1', description: 'The employees' } }, type: object }
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Invalid input'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:breaks'
  '/api/v1/time-tracking/break-policies/{id}/sync':
    put:
      tags:
        - 'Meal & Rest Breaks'
        - 'Public API'
      summary: 'Sync Break Policy'
      description: 'Performs a full replacement of a break policy and its related data (breaks and employee assignments). Unlike the partial update endpoint, this replaces the entire policy state with the provided payload, removing any breaks or assignments not included in the request.'
      operationId: sync-break-policy
      parameters:
        -
          name: id
          in: path
          description: 'The break policy ID.'
          required: true
          schema:
            type: string
            format: uuid
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TimeTracking-SyncTimeTrackingBreakPolicy-V1'
      responses:
        '200':
          description: 'Break policy synced successfully'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimeTracking-TimeTrackingBreakPolicyWithRelations-V1'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Break policy not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Invalid data provided'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:breaks.write'
  /api/v1/time-tracking/break-policies/suggestions:
    post:
      tags:
        - 'Meal & Rest Breaks'
        - 'Public API'
      summary: 'Get Break Policy Suggestions'
      description: 'Uses an AI agent to analyze existing break policies and company context, then returns structured meal and rest break policy recommendations ready for form pre-fill.'
      operationId: get-break-policy-suggestions
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - prompt
              properties:
                prompt:
                  description: 'The user''s request or question for break policy suggestions (e.g. "What policies do we need for our California employees?")'
                  type: string
                  minLength: 1
              type: object
      responses:
        '200':
          description: 'Suggestions returned successfully'
          content:
            application/json:
              schema:
                title: TimeTracking-BreakPolicySuggestionsResponse-V1
                properties:
                  suggestedPolicies: { type: array, items: { required: [id, title, breaks], properties: { id: { type: string }, title: { type: string }, description: { oneOf: [{ type: string }, { type: 'null' }] }, breaks: { type: array, items: { required: [name, duration, paid, availabilityType], properties: { name: { type: string }, duration: { type: integer }, paid: { type: boolean }, availabilityType: { type: string, enum: [anytime, hours_worked, time_of_day] }, availabilityMinHoursWorked: { oneOf: [{ type: number }, { type: 'null' }] } }, type: object } } }, type: object } }
                type: object
        '422':
          description: 'Invalid request — prompt is missing or empty'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error — agent workflow failed'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:breaks'
  /api/v1/time-tracking/projects:
    get:
      tags:
        - 'Time Tracking'
        - 'Public API'
      summary: 'List Time Tracking Projects'
      description: 'Returns a paginated list of time tracking projects. Supports OData-style `filter` and `sort` query parameters. Pagination is page-based via `page` and `pageSize` (defaults: page 1, pageSize 100, max 500).'
      operationId: list-projects
      parameters:
        -
          name: filter
          in: query
          description: 'OData v4 filter expression. Filterable fields: `id`, `name`, `billable`, `includeInPayroll`, `allEmployeesAssigned`, `archived`, `createdAt`, `updatedAt`.'
          required: false
          schema:
            type: string
        -
          name: sort
          in: query
          description: 'Sort expression like `name asc, createdAt desc`. Allowed fields: `name`, `createdAt`, `updatedAt`.'
          required: false
          schema:
            type: string
        -
          name: page
          in: query
          description: 'The starting page for pagination. Defaults to 1.'
          required: false
          schema:
            type: integer
            default: 1
            minimum: 1
        -
          name: pageSize
          in: query
          description: 'The number of items per page. Defaults to 100, maximum 500.'
          required: false
          schema:
            type: integer
            default: 100
            maximum: 500
            minimum: 1
      responses:
        '200':
          description: 'A paginated list of projects.'
          content:
            application/json:
              schema:
                title: Project-PaginatedTimeTrackingProjectsResponse-V1
                type: object
                allOf:
                  - { $ref: '#/components/schemas/Project-PaginatedResponseData-V1' }
                  - { properties: { data: { $ref: '#/components/schemas/Project-TimeTrackingProjectCollection-V1', description: 'The projects on the current page.' } }, type: object }
        '401':
          description: 'Unauthorized. Missing or invalid authentication.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden. Insufficient permissions.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Invalid query parameters (e.g., invalid `page`, `pageSize`, `filter`, or `sort`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:project'
    post:
      tags:
        - 'Time Tracking'
        - 'Public API'
      summary: 'Create Time Tracking Project'
      description: 'Creates a new time tracking project. Returns the newly created project resource on success.'
      operationId: create-project
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Project-CreateTimeTrackingProject-V1'
      responses:
        '201':
          description: 'Project created successfully.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project-TimeTrackingProject-V1'
        '400':
          description: 'Malformed request body.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '401':
          description: 'Unauthorized. Missing or invalid authentication.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden. Insufficient permissions.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '409':
          description: 'Conflict. A project with this name already exists.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Validation error (e.g. missing `name`, invalid `employeeIds`, name too long).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:project.write'
  '/api/v1/time-tracking/projects/{projectId}':
    get:
      tags:
        - 'Time Tracking'
        - 'Public API'
      summary: 'Get Time Tracking Project'
      description: 'Retrieves a single time tracking project by its ID, including the list of employees assigned to it.'
      operationId: get-project
      parameters:
        -
          name: projectId
          in: path
          description: 'The project ID.'
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Successfully retrieved the project.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project-TimeTrackingProject-V1'
        '401':
          description: 'Unauthorized. Missing or invalid authentication.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden. Insufficient permissions.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Project not found.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'The provided `projectId` is not a valid integer.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:project'
  '/api/v1/time-tracking/projects/{id}':
    delete:
      tags:
        - 'Time Tracking'
        - 'Public API'
      summary: 'Delete Time Tracking Project'
      description: 'Deletes a time tracking project by its ID.'
      operationId: delete-project
      parameters:
        -
          name: id
          in: path
          description: 'The project ID.'
          required: true
          schema:
            type: integer
      responses:
        '204':
          description: 'Project deleted successfully.'
        '401':
          description: 'Unauthorized. Missing or invalid authentication.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden. Insufficient permissions.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Project not found.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'The provided project ID is not a valid integer.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:project.write'
    patch:
      tags:
        - 'Time Tracking'
        - 'Public API'
      summary: 'Update Time Tracking Project'
      description: 'Partially updates a time tracking project identified by its ID. Only fields provided in the request body are updated; omitted fields are left unchanged.'
      operationId: update-project
      parameters:
        -
          name: id
          in: path
          description: 'The project ID.'
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Project-UpdateTimeTrackingProject-V1'
      responses:
        '200':
          description: 'Successfully updated the project.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project-TimeTrackingProject-V1'
        '400':
          description: 'Malformed request body.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '401':
          description: 'Unauthorized. Missing or invalid authentication.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden. Insufficient permissions.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Project not found.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '409':
          description: 'A project with the supplied name already exists.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Validation error (e.g., invalid `projectId`, no fields provided, invalid types, or setting `hasTasks=true` on a project with no active tasks).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:project.write'
  '/api/v1/time-tracking/projects/{projectId}/tasks':
    get:
      tags:
        - 'Time Tracking'
        - 'Public API'
      summary: 'List Time Tracking Project Tasks'
      description: 'Returns a paginated list of tasks for the specified time tracking project. Tasks are filtered by `statuses[]`, which defaults to `active`.'
      operationId: list-project-tasks
      parameters:
        -
          name: projectId
          in: path
          description: 'The project ID.'
          required: true
          schema:
            type: integer
        -
          name: 'statuses[]'
          in: query
          description: 'Statuses to include. Defaults to `active` (excludes deleted tasks). Use both values to include active and deleted tasks.'
          required: false
          explode: true
          schema:
            type: array
            items:
              type: string
              enum:
                - active
                - deleted
            default:
              - active
        -
          name: page
          in: query
          description: 'The page number to retrieve (1-indexed).'
          required: false
          schema:
            type: integer
            default: 1
            minimum: 1
        -
          name: pageSize
          in: query
          description: 'The maximum number of items per page.'
          required: false
          schema:
            type: integer
            default: 25
            maximum: 500
            minimum: 1
      responses:
        '200':
          description: 'Successfully retrieved tasks for the project.'
          content:
            application/json:
              schema:
                title: Project-PaginatedTasksResponse-V1
                required:
                  - data
                  - meta
                  - _links
                properties:
                  data: { $ref: '#/components/schemas/Project-TimeTrackingTaskCollection-V1', description: 'Array of task resources for the current page.' }
                  meta: { required: [totalItems, page, pageSize, totalPages], properties: { totalItems: { type: integer, minimum: 0, example: 42 }, page: { type: integer, minimum: 1, example: 1 }, pageSize: { type: integer, minimum: 1, example: 25 }, totalPages: { type: integer, minimum: 0, example: 2 } }, type: object }
                  _links: { required: [prev, next], properties: { prev: { properties: { href: { type: string, format: uri } }, oneOf: [{ type: object }, { type: 'null' }] }, next: { properties: { href: { type: string, format: uri } }, oneOf: [{ type: object }, { type: 'null' }] } }, type: object }
                type: object
        '401':
          description: 'Unauthorized. Missing or invalid authentication.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden. Insufficient permissions.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Project not found.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'A query parameter is invalid.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:project'
    post:
      tags:
        - 'Time Tracking'
        - 'Public API'
      summary: 'Create Time Tracking Project Task'
      description: 'Creates a new task on the specified time tracking project.'
      operationId: create-project-task
      parameters:
        -
          name: projectId
          in: path
          description: 'The project ID.'
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Project-CreateTimeTrackingProjectTask-V1'
      responses:
        '201':
          description: 'Task created.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project-TimeTrackingTask-V1'
        '403':
          description: Forbidden.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Project not found.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '409':
          description: 'Duplicate task name within the project.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Validation error.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:project.write'
  '/api/v1/time-tracking/tasks/{id}':
    get:
      tags:
        - 'Time Tracking'
        - 'Public API'
      summary: 'Get Time Tracking Task'
      description: 'Retrieves a single time tracking task by its ID.'
      operationId: get-task
      parameters:
        -
          name: id
          in: path
          description: 'The task ID.'
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Successfully retrieved the task.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project-TimeTrackingTask-V1'
        '401':
          description: 'Unauthorized. Missing or invalid authentication.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden. Insufficient permissions.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Task not found.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'The provided `id` is not a valid integer.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:project'
    delete:
      tags:
        - 'Time Tracking'
        - 'Public API'
      summary: 'Delete Time Tracking Task'
      description: 'Deletes a time tracking task by its ID.'
      operationId: delete-task
      parameters:
        -
          name: id
          in: path
          description: 'The task ID.'
          required: true
          schema:
            type: integer
      responses:
        '204':
          description: 'The task was deleted successfully.'
        '401':
          description: 'Unauthorized. Missing or invalid authentication.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden. Insufficient permissions.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Task not found.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'The provided `id` is not a valid integer.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:project.write'
    patch:
      tags:
        - 'Time Tracking'
        - 'Public API'
      summary: 'Update Time Tracking Task'
      description: 'Partially updates a time tracking task identified by its ID. Only fields provided in the request body are updated; at least one field must be provided.'
      operationId: update-task
      parameters:
        -
          name: id
          in: path
          description: 'The task ID.'
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Project-UpdateTimeTrackingProjectTask-V1'
      responses:
        '200':
          description: 'Task updated.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Project-TimeTrackingTask-V1'
        '403':
          description: Forbidden.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Task not found.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '409':
          description: 'Duplicate task name within the project.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Validation error.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:project.write'
  /api/v1/time-tracking/shift-differentials:
    get:
      tags:
        - 'Time Tracking'
        - 'Public API'
      summary: 'List Time Tracking Shift Differentials'
      description: 'Returns a paginated list of time tracking shift differentials. Supports OData-style `filter` and `sort` query parameters. Pagination is page-based via `page` and `pageSize` (defaults: page 1, pageSize 20, max 100). Archived rows are excluded by default; include them with `filter=archived eq true`. Soft-deleted rows are never returned.'
      operationId: list-shift-differentials
      parameters:
        -
          name: filter
          in: query
          description: 'OData v4 filter expression. Filterable fields: `name`, `rate`, `rateType`, `archived`.'
          required: false
          schema:
            type: string
        -
          name: sort
          in: query
          description: 'Sort expression like `name asc, createdAt desc`. Allowed fields: `name`, `createdAt`, `updatedAt`.'
          required: false
          schema:
            type: string
        -
          name: page
          in: query
          description: 'The starting page for pagination. Defaults to 1.'
          required: false
          schema:
            type: integer
            default: 1
            minimum: 1
        -
          name: pageSize
          in: query
          description: 'The number of items per page. Defaults to 20, maximum 100.'
          required: false
          schema:
            type: integer
            default: 20
            maximum: 100
            minimum: 10
      responses:
        '200':
          description: 'A paginated list of shift differentials.'
          content:
            application/json:
              schema:
                title: ShiftDifferential-PaginatedTimeTrackingShiftDifferentialsResponse-V1
                type: object
                allOf:
                  - { $ref: '#/components/schemas/ShiftDifferential-PaginatedResponseData-V1' }
                  - { properties: { data: { $ref: '#/components/schemas/ShiftDifferential-TimeTrackingShiftDifferentialCollection-V1', description: 'The shift differentials on the current page.' } }, type: object }
        '401':
          description: 'Unauthorized. Missing or invalid authentication.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden. Insufficient permissions.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Invalid query parameters (e.g., invalid `page`, `pageSize`, `filter`, or `sort`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:shift_differentials'
    post:
      tags:
        - 'Time Tracking'
        - 'Public API'
      summary: 'Create Time Tracking Shift Differential'
      description: 'Creates a new time tracking shift differential.'
      operationId: create-shift-differential
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ShiftDifferential-CreateTimeTrackingShiftDifferential-V1'
      responses:
        '201':
          description: 'Shift differential created successfully.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShiftDifferential-TimeTrackingShiftDifferential-V1'
        '400':
          description: 'Malformed request body.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '401':
          description: 'Unauthorized. Missing or invalid authentication.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden. Insufficient permissions.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '409':
          description: 'Conflict. A shift differential with the provided name already exists.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Validation error (e.g., missing `name`, invalid `rate`, empty `times` array).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:shift_differentials.write'
  '/api/v1/time-tracking/shift-differentials/{id}':
    get:
      tags:
        - 'Time Tracking'
        - 'Public API'
      summary: 'Get Time Tracking Shift Differential'
      description: 'Retrieves a single time tracking shift differential by its ID. Archived shift differentials are returned normally; soft-deleted shift differentials return 404.'
      operationId: get-shift-differential
      parameters:
        -
          name: id
          in: path
          description: 'The shift differential ID.'
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Successfully retrieved the shift differential.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShiftDifferential-TimeTrackingShiftDifferential-V1'
        '401':
          description: 'Unauthorized. Missing or invalid authentication.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden. Insufficient permissions.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Shift differential not found.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'The provided shift differential ID is not a valid integer.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:shift_differentials'
    delete:
      tags:
        - 'Time Tracking'
        - 'Public API'
      summary: 'Delete Time Tracking Shift Differential'
      description: 'Deletes a time tracking shift differential by its ID.'
      operationId: delete-shift-differential
      parameters:
        -
          name: id
          in: path
          description: 'The shift differential ID.'
          required: true
          schema:
            type: integer
      responses:
        '204':
          description: 'Shift differential deleted successfully.'
        '401':
          description: 'Unauthorized. Missing or invalid authentication.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden. Insufficient permissions.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'The provided shift differential ID is not a valid integer.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:shift_differentials.write'
    patch:
      tags:
        - 'Time Tracking'
        - 'Public API'
      summary: 'Update Time Tracking Shift Differential'
      description: 'Partially updates a time tracking shift differential identified by its ID.'
      operationId: update-shift-differential
      parameters:
        -
          name: id
          in: path
          description: 'The shift differential ID.'
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ShiftDifferential-UpdateTimeTrackingShiftDifferential-V1'
      responses:
        '200':
          description: 'Shift differential updated successfully.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ShiftDifferential-TimeTrackingShiftDifferential-V1'
        '400':
          description: 'Malformed request body.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '401':
          description: 'Unauthorized. Missing or invalid authentication.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden. Insufficient permissions.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Shift differential not found.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '409':
          description: 'Conflict. A shift differential with the provided name already exists.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Validation error (e.g., invalid `rate`, invalid `times`, no fields provided).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - 'time_tracking:shift_differentials.write'
  /api/v1/time_tracking/clock_entries/delete:
    post:
      tags:
        - 'Time Tracking'
        - 'Public API'
      summary: 'Delete Timesheet Clock Entries'
      description: 'Deletes one or more timesheet clock entries by their IDs. Delete operations are idempotent; deleting already-removed entries does not require client retries.'
      operationId: delete-timesheet-clock-entries-via-post
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ClockEntryIdsSchema'
      responses:
        '204':
          description: 'Entries deleted successfully. No content returned.'
        '400':
          description: 'Bad request parameters - missing or invalid clockEntryIds.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden. Insufficient permissions or timesheet already approved.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '409':
          description: 'One or more entries are still clocked in; clock out before deleting.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '412':
          description: 'Precondition failed - invalid company configuration or timezone.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Server error.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - time_tracking.write
  /api/v1/time_tracking/hour_entries/delete:
    post:
      tags:
        - 'Time Tracking'
        - 'Public API'
      summary: 'Delete Timesheet Hour Entries'
      description: 'Deletes one or more timesheet hour entries by their IDs. Delete operations are idempotent; deleting already-removed entries does not require client retries.'
      operationId: delete-timesheet-hour-entries-via-post
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/HourEntryIdsSchema'
      responses:
        '204':
          description: 'Entries deleted successfully. No content returned.'
        '400':
          description: 'Bad request parameters - missing or invalid hourEntryIds.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden. Insufficient user permissions or API access is not turned on.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Hour entry not found.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '409':
          description: 'Timesheet type conflict.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '412':
          description: 'Precondition failed - invalid time tracking configuration or timezone.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - time_tracking.write
  /api/v1/time_tracking/timesheet_entries:
    get:
      tags:
        - 'Time Tracking'
        - 'Public API'
      summary: 'List Timesheet Entries'
      description: 'Returns timesheet entries for all employees, or a filtered subset, within the specified date range. Results include both clock and hour entry types. Dates must fall within the last 365 days and are interpreted in the company timezone.'
      operationId: list-timesheet-entries
      parameters:
        -
          name: start
          in: query
          description: 'YYYY-MM-DD. Only show timesheet entries on/after the specified start date. Must be within the last 365 days.'
          required: true
          schema:
            type: string
            format: date
            example: '2025-01-01'
        -
          name: end
          in: query
          description: 'YYYY-MM-DD. Only show timesheet entries on/before the specified end date. Must be within the last 365 days.'
          required: true
          schema:
            type: string
            format: date
            example: '2025-03-01'
        -
          name: employeeIds
          in: query
          description: 'A comma separated list of employee IDs. When specified, only entries that match these employee IDs are returned. When omitted, entries for all accessible employees are returned.'
          required: false
          schema:
            type: string
            pattern: '^\d+(,\d+)*$'
            example: '1,2,3'
      responses:
        '200':
          description: 'Returns the matching timesheet entries grouped by employee.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmployeeTimesheetEntryCollectionTransformer'
        '400':
          description: 'Bad request parameters - invalid dates, date range exceeds 365 days, or malformed employeeIds.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Insufficient user permissions or API access is not turned on.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Server error.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - time_tracking
  '/api/v1/time_tracking/employees/{employeeId}/clock_in':
    post:
      tags:
        - 'Time Tracking'
        - 'Public API'
      summary: 'Create Timesheet Clock-In Entry'
      description: 'Clocks in an employee at the current server time. To record a historical clock-in, provide a `date`, `start` (HH:MM, 24-hour format), and `timezone`. You can optionally associate the entry with `projectId`, `taskId` (requires `projectId`), `breakId`, and a `note`.'
      operationId: create-timesheet-clock-in-entry
      parameters:
        -
          name: employeeId
          in: path
          description: 'ID of the employee to clock in.'
          required: true
          schema:
            type: integer
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ClockInRequestSchema'
      responses:
        '200':
          description: 'The employee was successfully clocked in. Returns the new clock entry details.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimesheetEntryInfoApiTransformer'
        '400':
          description: 'Bad request parameters - invalid date, time, timezone, project, task, or note.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden. Insufficient user permissions or API access is not turned on.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Employee not found.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '406':
          description: 'A future clock entry exists; cannot clock in until it is resolved.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '409':
          description: 'The employee is already clocked in, or timesheet type conflict.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '412':
          description: 'Precondition failed - invalid company configuration, invalid timezone, or employee is not configured for time tracking.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Server error.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - time_tracking.write
  '/api/v1/time_tracking/employees/{employeeId}/clock_out':
    post:
      tags:
        - 'Time Tracking'
        - 'Public API'
      summary: 'Create Timesheet Clock-Out Entry'
      description: 'Clocks out a currently clocked-in employee at the current server time. To record a historical clock-out, provide a `date`, `end` (HH:MM, 24-hour format), and `timezone`.'
      operationId: create-timesheet-clock-out-entry
      parameters:
        -
          name: employeeId
          in: path
          description: 'ID of the employee to clock out.'
          required: true
          schema:
            type: integer
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ClockOutRequestSchema'
      responses:
        '200':
          description: 'The employee was successfully clocked out. Returns the completed clock entry details.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimesheetEntryInfoApiTransformer'
        '400':
          description: 'Bad request parameters - invalid date, time, or timezone.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden. Insufficient user permissions or API access is not turned on.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Employee not found, or the clock entry has already been removed.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '409':
          description: 'The employee is not currently clocked in (already clocked out), or a timesheet type conflict.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '412':
          description: 'Precondition failed - invalid company configuration, invalid timezone, no open clock entries, or employee is not configured for time tracking.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Server error.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - time_tracking.write
  /api/v1/time_tracking/clock_entries/store:
    post:
      tags:
        - 'Time Tracking'
        - 'Public API'
      summary: 'Create or Update Timesheet Clock Entries'
      description: 'Creates or updates timesheet clock entries in bulk. Entries with an existing ID are updated; entries without an ID are created.'
      operationId: create-or-update-timesheet-clock-entries
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ClockEntriesSchema'
      responses:
        '201':
          description: 'Entries created or updated successfully. Returns the resulting clock entry details.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimesheetEntryInfoCollectionApiTransformer'
        '400':
          description: 'Bad request parameters - invalid data, clock times, project, task, or note.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden. Insufficient user permissions or API access is not turned on.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Employee or timesheet entry not found.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '406':
          description: 'Entry is beyond the end of the current period.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '409':
          description: 'Conflicting entries exist, or timesheet type conflict.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '412':
          description: 'Precondition failed - invalid company configuration or timezone.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - time_tracking.write
  /api/v1/time_tracking/hour_entries/store:
    post:
      tags:
        - 'Time Tracking'
        - 'Public API'
      summary: 'Create or Update Timesheet Hour Entries'
      description: 'Creates or updates timesheet hour entries in bulk. Entries with an existing ID are updated; entries without an ID are created.'
      operationId: create-or-update-timesheet-hour-entries
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/HourEntriesRequestSchema'
      responses:
        '201':
          description: 'Entries created or updated successfully. Returns the resulting hour entry details.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimesheetEntryInfoCollectionApiTransformer'
        '400':
          description: 'Bad request parameters - invalid hours, project, task, or note.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden. Insufficient user permissions or API access is not turned on.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Employee or timesheet entry not found.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '406':
          description: 'Request not acceptable.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '409':
          description: 'Timesheet type conflict.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '412':
          description: 'Precondition failed - invalid company configuration or timezone.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - time_tracking.write
  /api/v1/webhooks:
    get:
      tags:
        - Webhooks
        - 'Public API'
      summary: 'List Webhooks'
      description: 'Returns all webhooks owned by the authenticated user. Each entry is a summary with the webhook''s ID, name, URL, creation datetime, and last-fired datetime. Returns an empty array when no webhooks exist. Use "Get Webhook" to retrieve the full configuration of a specific webhook, including monitored fields and post fields when applicable, and events.'
      operationId: list-webhooks
      responses:
        '200':
          description: 'An object with a `webhooks` array containing one summary entry per webhook owned by the authenticated user.'
          content:
            application/json:
              schema:
                title: WebhooksList
                properties:
                  webhooks: { type: array, items: { properties: { id: { description: 'The ID of the webhook.', type: string, example: '1' }, name: { description: 'The name of the webhook.', type: string }, created: { description: 'Datetime when the webhook was created (UTC, format: YYYY-MM-DD HH:MM:SS).', type: string }, lastSent: { description: 'Datetime when the webhook was last fired (UTC, format: YYYY-MM-DD HH:MM:SS). Null if the webhook has never fired.', oneOf: [{ type: string }, { type: 'null' }] }, url: { description: 'The URL the webhook sends data to.', type: string } }, type: object } }
                type: object
        '401':
          description: 'Unauthorized. No response body is returned.'
        '500':
          description: 'Internal server error.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookError'
      security:
        -
          basic: []
        -
          oauth:
            - webhooks
    post:
      tags:
        - Webhooks
        - 'Public API'
      summary: 'Create Webhook'
      description: "Creates a new webhook for the authenticated user. The webhook will fire when the specified events occur or when any of the monitored fields change.\n\nThe `monitorFields` array is required when `events` includes `employee.updated` or `employee_with_fields.updated`. If `events` is omitted, it defaults to `['employee_with_fields.updated', 'employee_with_fields.deleted', 'employee_with_fields.created']`, which means `monitorFields` is required by default. The `format` field is required.\n\nThe response includes a `privateKey` that should be used to verify the authenticity of incoming webhook payloads via HMAC-SHA256. This key is only returned at creation time and cannot be retrieved again.\n\nFor more details refer to the [webhooks documentation](https://documentation.bamboohr.com/docs/webhooks), including guides for [event-based](https://documentation.bamboohr.com/docs/event-based-webhooks) and [field-based](https://documentation.bamboohr.com/docs/field-based-webhooks) webhooks.\n\nFor details on the payloads sent by each event, see the event reference:\n- [employee.created](https://documentation.bamboohr.com/reference/employee-created-webhook)\n- [employee.updated](https://documentation.bamboohr.com/reference/employee-updated-webhook)\n- [employee.deleted](https://documentation.bamboohr.com/reference/employee-deleted-webhook)"
      operationId: create-webhook
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NewWebHook'
      responses:
        '201':
          description: 'Webhook created successfully. The `privateKey` field is only returned in this response and cannot be retrieved again.'
          content:
            application/json:
              schema:
                title: Webhook
                properties:
                  id: { description: 'The ID of the webhook.', type: string, example: '4' }
                  name: { description: 'The name of the webhook.', type: string, example: 'Example Webhook' }
                  created: { description: 'Datetime when the webhook was created (UTC, format: YYYY-MM-DD HH:MM:SS).', type: string, example: '2021-09-20 22:38:01' }
                  lastSent: { description: 'Datetime when the webhook was last fired (UTC, format: YYYY-MM-DD HH:MM:SS). Null if the webhook has never fired.', example: '2021-09-20 22:38:01', oneOf: [{ type: string }, { type: 'null' }] }
                  monitorFields: { description: 'A list of fields to monitor. Null when the webhook is not configured to monitor fields (e.g. event-only webhooks).', example: [firstName, lastName], oneOf: [{ type: array, items: { type: string } }, { type: 'null' }] }
                  postFields: { description: 'An object map of field ID or alias to the external name used in the webhook payload.', type: object, default: {  }, example: { firstName: Name, lastName: Surname, dateOfBirth: DOB } }
                  url: { description: 'The URL the webhook sends data to.', type: string, example: 'https://www.example.com' }
                  format: { description: 'The payload format used by the webhook.', type: string, enum: [json, form-encoded], example: json }
                  privateKey: { description: 'The private key used to verify webhook authenticity via HMAC-SHA256. Only returned at creation time.', type: string }
                  includeCompanyDomain: { description: 'Whether the company domain is added to the webhook request header.', type: boolean, example: false }
                  events: { description: 'Events that trigger this webhook.', type: array, items: { $ref: '#/components/schemas/WebhookEventType' }, example: [employee.created, employee.updated, employee.deleted] }
                type: object
        '400':
          description: 'Request body is malformed or missing required fields. Common causes: missing `format`, missing `monitorFields` when required by the selected events, URL not starting with `https://`, or an invalid `format` value.'
          content:
            application/json:
              schema:
                title: CreateWebhookBadRequestResponse
                oneOf:
                  - { $ref: '#/components/schemas/WebhookBadRequest' }
                  - { $ref: '#/components/schemas/WebhookError' }
        '401':
          description: Unauthorized.
          content:
            application/json: {  }
        '403':
          description: 'The authenticated user does not have permission to access one or more of the specified fields. The response lists which `monitorFields`, `postFields`, and `unknownFields` caused the violation.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookError'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookError'
      security:
        -
          basic: []
        -
          oauth:
            - webhooks.write
        -
          oauth:
            - webhooks
  '/api/v1/webhooks/{id}':
    get:
      tags:
        - Webhooks
        - 'Public API'
      summary: 'Get Webhook'
      description: 'Returns the full configuration of a single webhook owned by the authenticated user, including its name, URL, format, monitored fields and post fields when applicable, events, creation datetime, and last-sent datetime. Returns 403 if the webhook exists but belongs to a different user, and 404 if the webhook does not exist.'
      operationId: get-webhook
      parameters:
        -
          name: id
          in: path
          description: 'The webhook ID to retrieve.'
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'The full configuration of the requested webhook.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebHookResponse'
        '401':
          description: Unauthorized.
          content:
            application/json: {  }
        '403':
          description: 'The webhook exists but belongs to a different user.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookError'
        '404':
          description: 'No webhook exists with the given ID.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookError'
        '500':
          description: 'Internal server error.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookError'
      security:
        -
          basic: []
        -
          oauth:
            - webhooks
    put:
      tags:
        - Webhooks
        - 'Public API'
      summary: 'Update Webhook'
      description: "Performs a full replacement update of a webhook — all request body fields overwrite existing values, so omitted optional fields revert to defaults. The `monitorFields` array must be non-empty when the webhook's events include `employee.updated` or `employee_with_fields.updated` (the default event set includes `employee_with_fields.updated`). The private key is not regenerated on update. Use List Webhooks to discover webhook IDs."
      operationId: update-webhook
      parameters:
        -
          name: id
          in: path
          description: 'The webhook ID to update.'
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NewWebHook'
      responses:
        '200':
          description: 'The updated webhook.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebHookResponse'
        '400':
          description: 'Request body is malformed or missing required fields. Common causes: missing `format`, missing `monitorFields` when required by the selected events, URL not starting with `https://`, or an invalid `format` value.'
          content:
            application/json:
              schema:
                title: UpdateWebhookBadRequestResponse
                oneOf:
                  - { $ref: '#/components/schemas/WebhookBadRequest' }
                  - { $ref: '#/components/schemas/WebhookError' }
        '401':
          description: Unauthorized.
        '403':
          description: 'The authenticated user does not have permission to access one or more of the specified fields, or does not have access to the webhook. When caused by field permission violations, `errors` is an array and lists which `monitorFields`, `postFields`, and `unknownFields` caused the violation. When caused by webhook ownership denial, `errors` is an object with a single `error` string (e.g. `{"errors":{"error":"You do not have access to webhook ID: 6"}}`).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookError'
        '404':
          description: 'The webhook to be updated does not exist.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookError'
        '500':
          description: 'Internal server error.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookError'
      security:
        -
          basic: []
        -
          oauth:
            - webhooks.write
        -
          oauth:
            - webhooks
    delete:
      tags:
        - Webhooks
        - 'Public API'
      summary: 'Delete Webhook'
      description: "Permanently removes a webhook owned by the authenticated user. A webhook can only be deleted by the same credentials that created it; attempting to delete another user's webhook returns 403. Use List Webhooks to find webhook IDs."
      operationId: delete-webhook
      parameters:
        -
          name: id
          in: path
          description: 'The webhook ID to delete.'
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Webhook deleted successfully. Returns a plain text confirmation message.'
          content:
            text/plain:
              schema:
                title: WebhookDeletedResponse
                type: string
                example: 'Web hook has been deleted.'
        '401':
          description: Unauthorized.
        '403':
          description: 'The authenticated user does not have permission to delete the requested webhook.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookError'
        '404':
          description: 'No webhook exists with the given ID.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookError'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookError'
      security:
        -
          basic: []
        -
          oauth:
            - webhooks.write
        -
          oauth:
            - webhooks
  '/api/v1/webhooks/{id}/log':
    get:
      tags:
        - Webhooks
        - 'Public API'
      summary: 'List Webhook Logs'
      description: "Returns an array of recent delivery log entries for a webhook. Use List Webhooks to find webhook IDs. Logs cover the last 14 days and are limited to 200 entries. The `lastAttempted` and `lastSuccess` fields are usually UTC datetimes in `YYYY-MM-DD HH:MM:SS` format, but may instead contain status strings such as `Webhook Not Found`. Returns an empty array if no deliveries have occurred in the lookback window.\n\n**Rate limiting:** This endpoint is rate-limited. When the rate limit is exceeded the server still returns HTTP 200, but the body is `{\"error\":{\"code\":429,\"message\":\"Over rate limit, please try again in 60 seconds\"}}` instead of the log array. Callers should check for this shape before processing the response as a log list."
      operationId: list-webhook-logs
      parameters:
        -
          name: id
          in: path
          description: 'The webhook ID to get logs about.'
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'An array of webhook delivery log entries for the last 14 days (up to 200 entries). Returns an empty array if no deliveries have occurred.'
          content:
            application/json:
              schema:
                title: WebhookLogListResponse
                oneOf:
                  - { title: WebhookLogList, type: array, items: { title: WebhookLogEntry, properties: { webhookId: { description: 'The ID of the webhook.', type: string }, url: { description: 'The URL the webhook was delivered to.', type: string }, lastAttempted: { description: 'Datetime of the last delivery attempt (UTC, format: YYYY-MM-DD HH:MM:SS), or a status string such as "Webhook Not Found" when the delivery target could not be reached.', type: string }, lastSuccess: { description: 'Datetime of the last successful delivery (UTC, format: YYYY-MM-DD HH:MM:SS), or a status string such as "Webhook Not Found" when no successful delivery has been recorded.', type: string }, statusCode: { description: 'HTTP status code returned by the webhook endpoint on the last delivery attempt.', type: string }, format: { description: 'The payload format used for this delivery.', type: string }, employeeIds: { description: 'Array of employee IDs included in the webhook payload, returned as strings.', type: array, items: { type: string }, example: ['135'] } }, type: object } }
                  - { title: WebhookLogRateLimitResponse, properties: { error: { properties: { code: { type: integer, example: 429 }, message: { type: string, example: 'Over rate limit, please try again in 60 seconds' } }, type: object } }, type: object }
        '403':
          description: 'The authenticated user does not have permission to see the requested webhook.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookError'
        '404':
          description: 'The webhook does not exist.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookError'
        '500':
          description: 'Internal error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookError'
        '503':
          description: 'Log search timed out. Returns a plain text error message. Try again later.'
          content:
            text/plain:
              schema:
                title: WebhookLogTimeoutResponse
                type: string
                example: 'Log search timed out. Try again later.'
      security:
        -
          basic: []
        -
          oauth:
            - webhooks
  /api/v1/webhooks/monitor_fields:
    get:
      tags:
        - Webhooks
        - 'Public API'
      summary: 'List Monitor Fields'
      description: 'Returns the list of employee fields that can be monitored by a webhook. Monitor fields are only applicable to webhooks that use update events (`employee.updated` or `employee_with_fields.updated`). Use the field IDs or aliases from this response in the `monitorFields` array when creating or updating a webhook via Create Webhook or Update Webhook.'
      operationId: list-monitor-fields
      responses:
        '200':
          description: 'An object with a `fields` array containing one entry per monitorable employee field, including the field ID (as a string), human-readable name, and alias (null if no alias is defined).'
          content:
            application/json:
              schema:
                title: MonitorFieldList
                required:
                  - fields
                properties:
                  fields: { description: 'The list of employee fields available for webhook monitoring.', type: array, items: { required: [id, name, alias], properties: { id: { description: 'The field ID, returned as a string.', type: string, example: '4175' }, name: { description: 'The human-readable field name.', type: string, example: 'Accrual Level Start Date' }, alias: { description: 'The field alias used in the BambooHR API (e.g. `firstName`). Null if no alias is defined for this field.', example: dateOfBirth, oneOf: [{ type: string }, { type: 'null' }] } }, type: object } }
                type: object
        '401':
          description: Unauthorized.
        '500':
          description: 'Internal server error.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookError'
      security:
        -
          basic: []
        -
          oauth:
            - webhooks
  /api/v1/webhooks/post-fields:
    get:
      tags:
        - Webhooks
        - 'Public API'
      summary: 'Get Webhook Post Fields'
      description: 'Returns an object containing the employee fields that can be included in the webhook post body for field-based webhooks. Also includes the related table and page records referenced by those fields. Use the field IDs or aliases from this response in the `postFields` map when creating or updating a field-based webhook.'
      operationId: get-post-fields
      responses:
        '200':
          description: 'An object with `fields`, `tables`, and `pages` arrays. `fields` contains the postable employee fields with their IDs, aliases, types, and associated page/table IDs. `tables` and `pages` contain the referenced metadata for those associations.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebHookPostFieldResponseObject'
        '403':
          description: 'Permission denied.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WebhookError'
      security:
        -
          basic: []
        -
          oauth:
            - webhooks
  '/api/v1/datasets/{datasetName}':
    post:
      tags:
        - Datasets
        - 'Public API'
      summary: 'Get Data from Dataset (v1)'
      description: "Deprecated. Use \"Get Data from Dataset (v2)\" instead.\n\nRetrieves records from the specified dataset using the fields, filters, sorting, grouping, and aggregations supplied in the request body. Provide field names in the `fields` array; use \"Get Fields from Dataset (v1.2)\" to discover available names. The response contains paginated rows under `data`, an `aggregations` array (empty when none requested), and a `pagination` block with page navigation links. Results default to page 1 with 500 records per page (maximum 1000).\n\nUse \"Get Field Options (v1.2)\" to retrieve valid filter values. Filter fields do not need to appear in the `fields` list. Future hires have a status of `Inactive`; include it in your status filter to retrieve them. For `options`-type fields using `includes`/`does_not_include`, pass the filter value as an array enclosed in square brackets, for example `[\"Full-Time\", \"Part-Time\"]`.\n\nWhen any requested fields are historical table fields, pass their entity names in `showHistory`; entity names are returned by \"Get Fields from Dataset (v1.2)\". Grouping (`groupBy`) currently supports only one field; when active, `data` becomes an object keyed by group value instead of an array. Sort priority follows the order of objects in `sortBy`. Aggregations accept a `defaultAggregation` applied to every field and/or per-field `overridingAggregations`.\n\n**Aggregations by field type:** text: count; date: count, min, max; int: count, min, max, sum, avg; bool: count; options: count; govIdText: count.\n\n**Filter operators by field type:** text: contains, does_not_contain, equal, not_equal, empty, not_empty; date: lt, lte, gt, gte (each accepts a YYYY-MM-DD date string or a relative object `{\"duration\": \"N\", \"unit\": \"days|weeks|months|years\"}` where duration is a number as a string — lt/lte are measured backward from today, gt/gte forward), equal, not_equal, empty, not_empty, last, next (relative object `{\"duration\": \"N\", \"unit\": \"days|weeks|months|years\"}`), range (object `{\"start\": \"YYYY-MM-DD\", \"end\": \"YYYY-MM-DD\"}`); int: equal, not_equal, gte, gt, lte, lt, empty, not_empty; bool: checked, not_checked; options: includes, does_not_include, empty, not_empty; govIdText: empty, not_empty."
      operationId: get-data-from-dataset-v1
      parameters:
        -
          name: datasetName
          in: path
          description: 'The machine-readable name of the dataset to query. Use "List Datasets (v1.2)" to discover available names.'
          required: true
          schema:
            type: string
        -
          name: page
          in: query
          description: 'The page number to retrieve. Defaults to 1.'
          required: false
          schema:
            type: integer
            default: 1
            minimum: 1
        -
          name: page_size
          in: query
          description: 'The number of records to retrieve per page. Defaults to 500. Maximum is 1000.'
          required: false
          schema:
            type: integer
            default: 500
            maximum: 1000
            minimum: 1
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DataRequest'
      responses:
        '200':
          description: 'Returns an object with `data` (array of record objects, or an object keyed by group value when `groupBy` is used), `aggregations` (array, empty when none requested), and `pagination`.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmployeeResponse'
        '400':
          description: 'Invalid or missing argument(s), such as an unrecognised operator, malformed filter, invalid dataset name, or empty request body.'
        '403':
          description: 'Insufficient permissions to access this dataset.'
        '500':
          description: 'An unexpected internal error occurred while retrieving data.'
      deprecated: true
      security:
        -
          basic: []
        -
          oauth:
            - report
  '/api/v1/custom-reports/{reportId}':
    get:
      tags:
        - 'Custom Reports'
        - 'Public API'
      summary: 'Get Report by ID'
      description: "Executes a saved custom report and returns its data using the report's configured fields and filters. The `data` array contains employee record objects whose keys are determined by the fields selected when the report was created — each object is a flat key-value map where keys are field names (e.g. `firstName`, `status`, `hireDate`) and values are strings or `null`. The `aggregations` array is empty unless the report's underlying dataset configuration includes aggregation rules. Use \"List Reports\" to discover available report IDs.\n\nResponse shape: Each element of `data` is a flat key-value object — field values are top-level keys (e.g. `row[\"firstName\"]`). This differs from `get-data-from-dataset-v2`, where field values are nested under a `fields` key (e.g. `row[\"fields\"][\"firstName\"]`).\n\nThere is no schema-only response from this endpoint.\n\nThe `pagination.total_records` value is the number of rows produced by the saved report after applying the report's configured filters, not necessarily the total number of employees in the account. Validate output before using in automated pipelines.\n\nResults default to page 1 with 500 records per page (maximum 1000). Out-of-range page numbers are clamped to the nearest valid page. Invalid or zero values for `page` and `page_size` fall back to their defaults."
      operationId: get-report-by-id
      parameters:
        -
          name: reportId
          in: path
          description: 'The numeric ID of the saved custom report to execute.'
          required: true
          schema:
            type: integer
        -
          name: page
          in: query
          description: 'The page number to retrieve. Defaults to 1.'
          required: false
          schema:
            type: integer
            default: 1
            minimum: 1
        -
          name: page_size
          in: query
          description: 'The number of records per page. Defaults to 500. Maximum is 1000.'
          required: false
          schema:
            type: integer
            default: 500
            maximum: 1000
            minimum: 1
      responses:
        '200':
          description: "Paginated report data with dynamic employee fields based on the report's configuration."
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmployeeResponse'
        '400':
          description: 'Invalid or missing argument(s). Returned when the request fails validation or contains a type error.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: 'Access denied. The caller does not have permission to view this report.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          description: 'Report not found. No saved custom report exists with the given ID.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: 'An unexpected error occurred while retrieving the report.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        -
          basic: []
        -
          oauth:
            - report
  /api/v1/datasets:
    get:
      tags:
        - Datasets
        - 'Public API'
      summary: 'List Datasets (v1)'
      description: "Deprecated. Use \"List Datasets (v1.2)\" instead.\n\nReturns the catalog of datasets available for querying via the Datasets API. Each entry includes a machine-readable `name` (used as the `datasetName` path parameter in \"Get Fields from Dataset (v1.2)\" and \"Get Data from Dataset\") and a human-readable `label`. Use this endpoint to discover which datasets the current account has access to before building queries.\n\nThe response includes `Deprecation` and `Link` headers pointing to the v1.2 successor endpoint."
      operationId: list-datasets-v1
      responses:
        '200':
          description: 'Returns an object containing a `datasets` array of available datasets.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DatasetResponseV1'
        '403':
          description: 'Forbidden - insufficient permissions to access datasets.'
        '500':
          description: 'Internal server error while retrieving datasets.'
      deprecated: true
      security:
        -
          basic: []
        -
          oauth:
            - report
  '/api/v1/datasets/{datasetName}/fields':
    get:
      tags:
        - Datasets
        - 'Public API'
      summary: 'Get Fields from Dataset (v1)'
      description: "Deprecated. Use \"Get Fields from Dataset (v1.2)\" instead.\n\nReturns a paginated list of field descriptors for the specified dataset. Each field includes its machine-readable `name`, human-readable `label`, `parentType`, `parentName`, and `entityName`. Use the returned field `name` values in the `fields` array when querying data via \"Get Data from Dataset\". Use \"List Datasets (v1.2)\" to discover valid dataset names.\n\nPagination defaults to page 1 with 500 fields per page (maximum 1000). Out-of-range page numbers are clamped to the nearest valid page. The `next_page` and `prev_page` links in the pagination object are absolute URLs.\n\nError responses (400, 403, 500) return plain-text bodies, not JSON."
      operationId: get-fields-from-dataset-v1
      parameters:
        -
          name: datasetName
          in: path
          description: 'The machine-readable name of the dataset to retrieve fields for. Use "List Datasets (v1.2)" to discover valid names.'
          required: true
          schema:
            type: string
        -
          name: page
          in: query
          description: 'The page number to retrieve. Out-of-range values are clamped to the nearest valid page. Defaults to 1.'
          required: false
          schema:
            type: integer
            default: 1
            minimum: 1
        -
          name: page_size
          in: query
          description: 'The number of field records to retrieve per page. Defaults to 500. Maximum is 1000.'
          required: false
          schema:
            type: integer
            default: 500
            maximum: 1000
            minimum: 1
      responses:
        '200':
          description: 'Returns an object containing pagination metadata, the dataset name and label, and a `fields` array of field descriptors.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DatasetFieldsResponse'
        '400':
          description: 'The specified dataset name was not found. The body is a plain-text message.'
        '403':
          description: 'Insufficient permissions to access dataset fields. The body is a plain-text message.'
        '500':
          description: 'Internal server error while fetching the dataset configuration. The body is a plain-text message.'
      deprecated: true
      security:
        -
          basic: []
        -
          oauth:
            - report
  /api/v1/custom-reports:
    get:
      tags:
        - 'Custom Reports'
        - 'Public API'
      summary: 'List Reports'
      description: "Returns a paginated list of saved custom reports available in the account. Each report entry contains an `id` (integer) and a `name` (string). Pass a report's `id` to \"Get Report by ID\" to execute it and retrieve its data.\n\nResults default to page 1 with 500 records per page (maximum 1000). Out-of-range page numbers are clamped to the nearest valid page rather than returning an error. Invalid or zero values for `page` and `page_size` fall back to their defaults.\n\nThe `pagination` object includes `total_records`, `current_page`, `total_pages`, and nullable `next_page`/`prev_page` links. When there is no next or previous page the corresponding value is `null`."
      operationId: list-reports
      parameters:
        -
          name: page
          in: query
          description: 'The page number to retrieve. Out-of-range values are clamped to the nearest valid page. Defaults to 1.'
          required: false
          schema:
            type: integer
            default: 1
            minimum: 1
        -
          name: page_size
          in: query
          description: 'The number of records to retrieve per page. Defaults to 500. Maximum is 1000.'
          required: false
          schema:
            type: integer
            default: 500
            maximum: 1000
            minimum: 1
      responses:
        '200':
          description: 'Returns an object containing a `reports` array of report objects and a `pagination` object with page navigation metadata.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ReportsResponse'
        '403':
          description: 'The required feature toggle is not enabled for this account.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '500':
          description: 'An unexpected error occurred while retrieving the list of reports.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
      security:
        -
          basic: []
        -
          oauth:
            - report
  '/api/v1/datasets/{datasetName}/field-options':
    post:
      tags:
        - Datasets
        - 'Public API'
      summary: 'Get Field Options (v1)'
      description: "Deprecated. Use \"Get Field Options (v1.2)\" instead.\n\nReturns the allowed values for one or more fields in a dataset, for use as filter values when querying data via \"Get Data from Dataset\". Pass field names in the `fields` array of the request body; the response is an object keyed by field name, where each value is an array of `{id, value}` option objects. Optionally supply `filters` to narrow the returned options (e.g. only options that exist for active employees) and `dependentFields` when one field's options depend on another field's selected value.\n\nUnrecognised field names may produce a `500` response with a plain-text body rather than structured JSON."
      operationId: get-field-options-v1
      parameters:
        -
          name: datasetName
          in: path
          description: 'The name of the dataset to retrieve field options for.'
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FieldOptionsRequestSchema'
      responses:
        '200':
          description: 'An object keyed by field name, where each value is an array of available option objects for that field.'
          content:
            application/json:
              schema:
                title: FieldOptionsResponseV1
                type: object
                additionalProperties:
                  type: array
                  items: { $ref: '#/components/schemas/FieldOptionsTransformer' }
              example:
                status:
                  - { id: Active, value: Active }
                  - { id: Inactive, value: Inactive }
        '400':
          description: 'Bad request - missing or invalid fields, filters, or request format.'
          content:
            application/json:
              schema:
                title: BadRequestV1
                properties:
                  error: { properties: { code: { type: integer, example: 0 }, message: { type: string, example: 'fields or groupBy missing' } }, type: object }
                type: object
        '403':
          description: 'Forbidden - insufficient permissions to access this dataset.'
          content:
            application/json:
              schema:
                title: ForbiddenV1
                properties:
                  error: { properties: { code: { type: integer, example: 403 }, message: { type: string, example: 'You do not have permission to access this resource' } }, type: object }
                type: object
        '500':
          description: 'Internal server error. The response body may be plain text rather than a structured JSON payload.'
      deprecated: true
      security:
        -
          basic: []
        -
          oauth:
            - field
  /api/v1_2/datasets:
    get:
      tags:
        - Datasets
        - 'Public API'
      summary: 'List Datasets (v1.2)'
      description: "Returns the catalog of datasets available for querying via the Datasets API. Each entry includes a machine-readable `name` (used as the `datasetName` path parameter in \"Get Fields from Dataset (v1.2)\" and \"Get Data from Dataset (v2)\") and a human-readable `label`. Use this endpoint to discover which datasets the current account has access to before building queries.\n\nDatasets are queryable reporting datasets for the Datasets API. They are not the same as saved custom reports (`list-reports`), employee tabular table aliases (`list-tabular-fields`), or account list-field definitions (`list-list-fields`).\n\nNote: Compared to \"List Datasets (v1)\", this version returns RFC 7807 `application/problem+json` error responses and includes an `X-Request-ID` correlation header."
      operationId: list-datasets-v1-2
      responses:
        '200':
          description: 'Returns an object containing a `datasets` array of available datasets.'
          headers:
            X-Request-ID:
              description: 'Correlation ID for request tracing'
              schema:
                type: string
          content:
            application/json:
              schema:
                title: 'Datasets Response (v1.2)'
                properties:
                  datasets: { description: 'Array of available datasets', type: array, items: { properties: { name: { description: 'Machine-readable dataset identifier, used as the datasetName path parameter in other Datasets endpoints.', type: string, example: employee }, label: { description: 'Human-readable display name for the dataset.', type: string, example: Employee } }, type: object } }
                type: object
        '403':
          description: 'Forbidden - insufficient permissions to access datasets.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error while retrieving datasets.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - report
  '/api/v1_2/datasets/{datasetName}/fields':
    get:
      tags:
        - Datasets
        - 'Public API'
      summary: 'Get Fields from Dataset (v1.2)'
      description: "Returns a paginated list of field descriptors for the specified dataset. Each field includes its machine-readable `name`, human-readable `label`, parent section type and name, and `entityName`. Use the returned field `name` values in the `fields` array when querying data via \"Get Data from Dataset (v2)\". Use \"List Datasets (v1.2)\" to discover valid `datasetName` values. Pagination defaults to page 1 with 500 fields per page (maximum 1000). Out-of-range page numbers are clamped to the nearest valid page. Some datasets, especially `employee`, may return hundreds of fields. This endpoint does not support filtering by parent/entity/type; retrieve all pages and group client-side.\n\nThe `employee` dataset field names are fully qualified with their section prefix and differ from the field names used by `list-employees` and `get-employee`. Do not assume a field name from one endpoint works in the other.\n\nFor fields whose type is `list`, `multilist`, or another option-backed type, the field `id` can be matched to `fieldId` from `list-list-fields` to retrieve the account-level option list.\n\nNote: Compared to \"Get Fields from Dataset (v1)\", this version returns RFC 7807 `application/problem+json` error responses and includes an `X-Request-ID` correlation header."
      operationId: get-fields-from-dataset-v1-2
      parameters:
        -
          name: datasetName
          in: path
          description: 'The name of the dataset to retrieve fields for. Use "List Datasets (v1.2)" to discover valid names.'
          required: true
          schema:
            type: string
        -
          name: page
          in: query
          description: 'The page number to retrieve. Defaults to 1.'
          required: false
          schema:
            type: integer
            default: 1
            minimum: 1
        -
          name: page_size
          in: query
          description: 'The number of records to retrieve per page. Defaults to 500. Maximum is 1000.'
          required: false
          schema:
            type: integer
            default: 500
            maximum: 1000
            minimum: 1
      responses:
        '200':
          description: 'Returns an object containing pagination metadata, the dataset name and label, and a `fields` array of field descriptors.'
          headers:
            X-Request-ID:
              description: 'Correlation ID for request tracing. Echoes the request header value if provided, otherwise generated server-side.'
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/DatasetFieldsResponse'
        '403':
          description: 'Insufficient permissions to access dataset fields. Uses RFC 7807 problem+json.'
          headers:
            X-Request-ID:
              description: 'Correlation ID for request tracing.'
              schema:
                type: string
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'The specified dataset name was not found. The `code` field is `DATASET_NOT_FOUND`.'
          headers:
            X-Request-ID:
              description: 'Correlation ID for request tracing.'
              schema:
                type: string
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error while fetching the dataset configuration.'
          headers:
            X-Request-ID:
              description: 'Correlation ID for request tracing.'
              schema:
                type: string
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - report
  '/api/v1_2/datasets/{datasetName}/field-options':
    post:
      tags:
        - Datasets
        - 'Public API'
      summary: 'Get Field Options (v1.2)'
      description: "Returns the allowed values for one or more fields in a dataset, for use as filter values when querying data via \"Get Data from Dataset (v2)\". Pass field names in the `fields` array of the request body; the response is an object keyed by field name, where each value is an array of `{id, value}` option objects. Optionally supply `filters` to narrow the returned options (e.g. only options that exist for active employees) and `dependentFields` when one field's options depend on another field's selected value. Use \"Get Fields from Dataset (v1.2)\" to discover valid field names for a dataset.\n\nUse this endpoint when you already have dataset field names from `get-fields-from-dataset-v1-2` and need valid filter/display options for those specific dataset fields. It is dataset-scoped: field names must be dataset field name values such as `jobInformationDepartment`, not account list aliases such as `department`. \n\nNote: Compared to \"Get Field Options (v1)\", error responses (`400`, `403`) use RFC 7807 `application/problem+json` format. Unrecognised field names may produce a `500` response with a plain-text body rather than structured JSON."
      operationId: get-field-options-v1-2
      parameters:
        -
          name: datasetName
          in: path
          description: 'The name of the dataset you want to see field options for. Use "List Datasets (v1.2)" to discover available dataset names.'
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/FieldOptionsRequestSchema'
      responses:
        '200':
          description: 'An object keyed by field name, where each value is an array of available option objects for that field.'
          content:
            application/json:
              schema:
                title: FieldOptionsResponseV1_2
                type: object
                additionalProperties:
                  type: array
                  items: { $ref: '#/components/schemas/FieldOptionsTransformer' }
              example:
                status:
                  - { id: Active, value: Active }
                  - { id: Inactive, value: Inactive }
        '400':
          description: 'Bad request - invalid fields, filters, or request format. Uses RFC 7807 problem+json.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden - insufficient permissions to access this dataset. Uses RFC 7807 problem+json.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error. The response body may be plain text rather than a structured payload.'
      security:
        -
          basic: []
        -
          oauth:
            - field
      x-mcp-annotations:
        readOnlyHint: true
        destructiveHint: false
        openWorldHint: false
  '/api/v2/datasets/{datasetName}/data':
    post:
      tags:
        - Datasets
        - 'Public API'
      summary: 'Get Data from Dataset (v2)'
      description: "Queries the named dataset and returns the matching rows. The response is a JSON object with `data` (an array of row objects, each wrapping its values under a `fields` object whose keys match the requested field names), `links` (pagination navigation URLs; `prev` and/or `next`, omitted at boundaries), and `meta` (with `page`, `pageSize`, `totalPages`, `totalItems`).\n\nUse this for tabular reporting, custom field selection across many employees, and OData-style filtering against the full dataset field catalog. For lightweight directory-style employee listings or batch lookups by ID, use List Employees (`list-employees`) instead. For the full set of fields on a single employee, use Get Employee (`get-employee`) instead.\n\nDiscover valid `datasetName` values via List Datasets (v1.2) (`list-datasets-v1-2`). Discover valid field names for a dataset via Get Fields from Dataset (v1.2) (`get-fields-from-dataset-v1-2`); the field set is dataset-specific.\n\nField-name vocabulary differs from the dedicated employee endpoints. The `employee` dataset uses fully qualified names such as `jobInformationJobTitle`, `jobInformationDepartment`, `jobInformationReportsTo`, and `employmentStatus`, where List Employees / Get Employee use shorter names like `jobTitleName`, `department`, `reportsTo`, and `status`. Always source field names from Get Fields from Dataset (v1.2) — names from the employee endpoints will not necessarily resolve here.\n\nFilter expressions use OData-style syntax with comparison operators `eq`, `ne`, `lt`, `le`, `gt`, `ge`, the list-membership operator `in (...)`, and logical operators `and` / `or`. The parser does not support `not in`; use `ne` for the inverse of `eq`, and on options-type fields `ne` already applies set-membership negation under the hood. A single filter expression may use `and` or `or`, but not both; mixed logical expressions return `422` with code `INVALID_FILTER`. Operator support varies by field type: `eq` / `ne` work on text, numeric, date, and options-type (single-select enum) fields like `status`, `employmentStatus`, and `jobInformationDepartment`; ordering operators `lt`, `le`, `gt`, `ge` are supported on numeric and date fields. The list operator `in` is the natural fit on options-type fields (e.g. `status in ('Active','Inactive')`), and is also accepted on text, numeric, and date fields when used with a single value (e.g. `firstName in ('Ashley')`, equivalent to `firstName eq 'Ashley'`); multi-value `in` on non-options fields returns `422`. To check a field's type, see the `type` value returned by Get Fields from Dataset (v1.2). Filter fields do not need to appear in the `fields` array; rows are filtered server-side. String literals in filter expressions are wrapped in single quotes, e.g. `firstName eq 'Ashley'` or `status in ('Active','Inactive')`.\n\n`orderBy` is a comma-separated list of `<field> <asc|desc>` rules. Every field used in `orderBy` must also appear in the `fields` array; omitting it returns a `400` with code `UE-1001`. `orderBy` is optional; with no `orderBy` the row order is unspecified.\n\nField-level access is enforced server-side based on the authenticated caller's permissions and OAuth scopes. Callers without permission to a requested field receive an error from the semantic layer rather than silent omission of the field from rows; callers without dataset-level access receive `403`. There is no per-row redaction within the response — a row that includes the caller in its access scope returns all requested fields, and a dataset the caller cannot see at all returns `404` (\"not accessible\" rather than disclosing existence).\n\nPagination: `page` defaults to `1` and `pageSize` defaults to `100` (maximum `1000`). `meta.totalItems` is the count of rows matching the query across all pages, `meta.totalPages` is `ceil(totalItems / pageSize)`, and `data` contains at most `pageSize` rows for the requested page. `links.next` is present when more pages exist; `links.prev` is present when not on the first page. Errors follow RFC 7807 (`application/problem+json`).\n\nNote: Compared to Get Data from Dataset (v1) (`get-data-from-dataset-v1`), this version uses `filter` and `orderBy` request fields instead of the legacy `filters` and `sortBy` structures, moves pagination inputs to body fields (`page`, `pageSize`) instead of query parameters, returns pagination in `links` / `meta` objects instead of a legacy `pagination` object, and returns RFC 7807 compliant error responses with an `X-Request-ID` correlation header."
      operationId: get-data-from-dataset-v2
      parameters:
        -
          name: datasetName
          in: path
          description: 'Machine-readable name of the dataset to query. Discover valid values via List Datasets (v1.2) (`list-datasets-v1-2`); examples include `employee`, `benefit`, and `applicants`. Unknown or inaccessible names return `404`.'
          required: true
          schema:
            type: string
          example: employee
      requestBody:
        description: 'Request parameters for dataset data retrieval'
        required: true
        content:
          application/json:
            schema:
              required:
                - fields
              properties:
                fields:
                  description: "Field names to return for each row. Names are dataset-specific; discover valid names via Get Fields from Dataset (v1.2) (`get-fields-from-dataset-v1-2`). The field-name vocabulary differs from the dedicated employee endpoints (e.g., the `employee` dataset uses `jobInformationReportsTo` / `jobInformationDepartment` / `employmentStatus` rather than List Employees' `reportsTo` / `department` / `status`)."
                  type: array
                  items: { type: string }
                  example: [firstName, lastName, employmentStatus]
                filter:
                  description: "OData-style filter expression. Comparison operators: `eq`, `ne`, `lt`, `le`, `gt`, `ge`. List-membership operator: `in (...)` (the parser does not support `not in`; use `ne` for the inverse). Logical operators: `and`, `or`. A single filter expression may use `and` or `or`, but not both; mixed logical expressions return `422` with code `INVALID_FILTER`. Operator support varies by field type. `eq` / `ne` work on text, numeric, date, and options-type (single-select enum) fields. Ordering operators (`lt`, `le`, `gt`, `ge`) work on numeric and date fields. `in (...)` is the natural fit on options-type fields like `status`, `employmentStatus`, and `jobInformationDepartment`, and is also accepted on text, numeric, and date fields when the list has a single value (equivalent to `eq`). Multi-value `in` on non-options fields returns `422`. To check a field's type, see the `type` value returned by Get Fields from Dataset (v1.2). String literals are wrapped in single quotes. Filter fields do not need to appear in `fields`."
                  example: "firstName eq 'Ashley' or firstName eq 'Charlotte'"
                  oneOf: [{ type: string }, { type: 'null' }]
                orderBy:
                  description: 'Comma-separated sort rules; each rule is a field name followed by `asc` or `desc`. Every field referenced in `orderBy` must also appear in `fields` — otherwise the request is rejected with `400` and code `UE-1001`. Omit to leave row order unspecified.'
                  example: 'lastName asc, firstName desc'
                  oneOf: [{ type: string }, { type: 'null' }]
                page:
                  description: 'Page number to retrieve (1-indexed). Defaults to 1.'
                  type: integer
                  default: 1
                  minimum: 1
                  example: 1
                pageSize:
                  description: 'Number of records per page. Defaults to 100. Maximum is 1000.'
                  type: integer
                  default: 100
                  maximum: 1000
                  minimum: 1
                  example: 25
              type: object
      responses:
        '200':
          description: 'Object containing `data` (array of record objects each wrapping requested values under a `fields` object), `links` (pagination URLs; `prev` and/or `next`, omitted at boundaries), and `meta` (`page`, `pageSize`, `totalPages`, `totalItems`).'
          headers:
            X-Request-ID:
              description: 'Correlation ID for request tracing. Echoes the client-provided value or a generated identifier.'
              schema:
                type: string
          content:
            application/json:
              schema:
                title: DatasetDataResponseV2
                properties:
                  data: { description: 'Array of record objects. Each object contains a `fields` object whose keys are the requested field names. Values may be strings, numbers, booleans, arrays/objects, or null depending on the dataset field and value.', type: array, items: { properties: { fields: { type: object, additionalProperties: { anyOf: [{ type: string }, { type: number }, { type: boolean }, { type: array, items: {  } }, { type: object }, { type: 'null' }] } } }, type: object } }
                  links: { description: 'Pagination navigation links. Contains `next` and/or `prev` URLs when applicable.', properties: { next: { oneOf: [{ type: string }, { type: 'null' }] }, prev: { oneOf: [{ type: string }, { type: 'null' }] } }, type: object }
                  meta: { description: 'Pagination metadata.', properties: { page: { type: integer }, pageSize: { type: integer }, totalPages: { type: integer }, totalItems: { type: integer } }, type: object }
                type: object
        '400':
          description: 'Missing or invalid parameters.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '401':
          description: Unauthorized.
        '403':
          description: 'Insufficient permissions to access the requested dataset.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'The specified dataset does not exist or is not accessible.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '413':
          description: 'The request produced too much data.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Validation failed, such as an invalid filter expression or pagination value.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - report
      x-mcp-annotations:
        readOnlyHint: true
        destructiveHint: false
        openWorldHint: false
  /api/v1/applicant_tracking/applications:
    get:
      tags:
        - 'Public API'
        - 'Applicant Tracking'
      summary: 'Get Job Applications'
      description: 'Get a list of applications. The owner of the API key used must have access to ATS settings. Combine as many different optional parameter filters as you like.'
      operationId: get-applications
      parameters:
        -
          name: page
          in: query
          description: 'The page number'
          schema:
            type: integer
        -
          name: jobId
          in: query
          description: 'A Job Id to limit results to'
          schema:
            type: integer
        -
          name: applicationStatusId
          in: query
          description: 'One or more application status IDs to filter by, comma-separated (e.g. `1,2,3`).'
          schema:
            type: string
        -
          name: applicationStatus
          in: query
          description: 'One or more application status group codes to filter by, comma-separated (e.g. `NEW,ACTIVE`). Allowed values: `ALL`, `ALL_ACTIVE`, `NEW`, `ACTIVE`, `INACTIVE`, `HIRED`.'
          schema:
            type: string
        -
          name: jobStatusGroups
          in: query
          description: 'One or more position status groups to filter by, comma-separated (e.g. `Draft,Open`). Allowed values: `ALL`, `DRAFT_AND_OPEN`, `Open`, `Filled`, `Draft`, `Deleted`, `On Hold`, `Canceled`.'
          schema:
            type: string
        -
          name: searchString
          in: query
          description: 'A general search criteria by which to find applications.'
          schema:
            type: string
        -
          name: sortBy
          in: query
          description: 'A specific field to sort the results by.'
          schema:
            type: string
            enum:
              - first_name
              - job_title
              - rating
              - phone
              - status
              - last_updated
              - created_date
        -
          name: sortOrder
          in: query
          description: 'Order by which to sort results.'
          schema:
            type: string
            enum:
              - ASC
              - DESC
        -
          name: newSince
          in: query
          description: 'Only return applications submitted after this UTC timestamp. Format: `Y-m-d H:i:s` (e.g. `2024-01-01 13:00:00`).'
          schema:
            type: string
            example: '2024-01-01 13:00:00'
      responses:
        '200':
          description: 'Success. Returns a paginated list of applications.'
          content:
            application/json:
              schema:
                title: ApplicationsList
                properties:
                  paginationComplete: { description: 'Indicates whether there are more pages of results available', type: boolean }
                  nextPageUrl: { description: 'URL to fetch the next page of results, or null if there are no more results', oneOf: [{ type: string }, { type: 'null' }] }
                  applications: { description: 'List of application objects', type: array, items: { properties: { id: { description: 'Application ID', type: integer }, appliedDate: { description: 'ISO 8601 datetime when the application was submitted', type: string, format: date-time }, status: { description: 'Current status of the application', properties: { id: { description: 'Status ID', type: integer }, label: { description: 'Status name', type: string } }, type: object }, rating: { description: 'Candidate rating (1-5) or null if not rated', oneOf: [{ type: integer }, { type: 'null' }] }, applicant: { description: 'Information about the applicant', properties: { id: { description: 'Applicant ID', type: integer }, firstName: { description: "Applicant's first name", type: string }, lastName: { description: "Applicant's last name", type: string }, avatar: { description: "URL to applicant's avatar image, or null", oneOf: [{ type: string }, { type: 'null' }] }, email: { description: "Applicant's email address", type: string }, source: { description: 'Source of the application', type: string } }, type: object }, job: { description: 'Information about the job position', properties: { id: { description: 'Job position ID', type: integer }, title: { description: 'Job position title', properties: { id: { description: 'Title ID, or null if not set', oneOf: [{ type: integer }, { type: 'null' }] }, label: { description: 'Title text', type: string } }, type: object } }, type: object } }, type: object } }
                type: object
        '400':
          description: 'Bad request parameters.'
          content:
            application/json: {  }
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json: {  }
        '403':
          description: 'Insufficient user permissions or API access is not turned on.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - 'hiring:applications'
  /api/v1/applicant_tracking/statuses:
    get:
      tags:
        - 'Public API'
        - 'Applicant Tracking'
      summary: 'Get Applicant Statuses'
      description: 'Get a list of applicant statuses configured for the company. The owner of the API key used must have access to ATS settings. Returns both system-defined and custom statuses.'
      operationId: get-statuses
      responses:
        '200':
          description: 'Success. Returns an array of applicant status objects.'
          content:
            application/json:
              schema:
                title: ApplicantStatusList
                type: array
                items:
                  title: ApplicantStatus
                  properties: { id: { description: 'Status ID', type: string }, code: { description: 'Internal status code, or null for custom statuses', oneOf: [{ type: string }, { type: 'null' }] }, name: { description: 'Status display name', type: string }, translatedName: { description: 'Translated display name', type: string }, description: { description: 'Optional description of the status', oneOf: [{ type: string }, { type: 'null' }] }, enabled: { description: 'Whether the status is currently active and selectable', type: boolean }, manageable: { description: 'Whether the status can be managed (edited/deleted) by the company', type: boolean } }
                  type: object
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json: {  }
        '403':
          description: 'Insufficient user permissions or API access is not turned on.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - 'hiring:applications'
  /api/v1/applicant_tracking/locations:
    get:
      tags:
        - 'Public API'
        - 'Applicant Tracking'
      summary: 'Get Company Locations'
      description: 'Get all company locations available for use when creating a job opening. The owner of the API key used must have access to ATS settings. Use the returned location IDs as the `jobLocation` field when calling the Create Job Opening endpoint.'
      operationId: get-company-locations
      responses:
        '200':
          description: 'Success. Returns an array of location objects.'
          content:
            application/json:
              schema:
                title: LocationsList
                type: array
                items:
                  $ref: '#/components/schemas/Location'
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json: {  }
        '403':
          description: 'Insufficient user permissions or API access is not turned on.'
          content:
            application/json: {  }
        '404':
          description: 'Bad request url.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - 'hiring:applications'
  /api/v1/applicant_tracking/hiring_leads:
    get:
      tags:
        - 'Public API'
        - 'Applicant Tracking'
      summary: 'Get Hiring Leads'
      description: 'Get the list of employees who can be assigned as a hiring lead when creating a new job opening. The owner of the API key used must have access to ATS settings. Use the returned `employeeId` values as the `hiringLead` field when calling the Create Job Opening endpoint.'
      operationId: get-hiring-leads
      responses:
        '200':
          description: 'Success. Returns an array of employees who can be assigned as hiring leads.'
          content:
            application/json:
              schema:
                title: HiringLeadsList
                type: array
                items:
                  title: HiringLead
                  properties: { employeeId: { description: 'The employee ID to use as the `hiringLead` value in the Create Job Opening request', type: integer }, preferredFullName: { description: "The employee's preferred full name", type: string } }
                  type: object
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json: {  }
        '403':
          description: 'Insufficient user permissions or API access is not turned on.'
          content:
            application/json: {  }
        '404':
          description: 'Bad request url.'
          content:
            application/json: {  }
      security:
        -
          basic: []
  /api/v1/applicant_tracking/application:
    post:
      tags:
        - 'Public API'
        - 'Applicant Tracking'
      summary: 'Create Candidate'
      description: "Create a new candidate application for a job opening. The owner of the API key used must have access to ATS settings. On success, returns the new candidate ID. Only fields required by the target job opening's standard questions need to be provided beyond firstName, lastName, and jobId."
      operationId: create-candidate
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              required:
                - firstName
                - jobId
                - lastName
              properties:
                firstName:
                  description: 'The first name of the candidate.'
                  type: string
                lastName:
                  description: 'The last name of the candidate.'
                  type: string
                email:
                  description: 'The email address of the candidate. Must be a valid email address.'
                  type: string
                  format: email
                phoneNumber:
                  description: 'The phone number of the candidate.'
                  type: string
                source:
                  description: 'The source of the candidate application, e.g. LinkedIn, Indeed, etc.'
                  type: string
                jobId:
                  description: 'The id of the job opening for the candidate application.'
                  type: integer
                address:
                  description: 'The street address of the candidate.'
                  type: string
                city:
                  description: 'The city of the candidate.'
                  type: string
                state:
                  description: 'The state or province of the candidate. Accepts state name, abbreviation, or ISO code.'
                  type: string
                zip:
                  description: 'The zip code or postal code of the candidate.'
                  type: string
                country:
                  description: 'The country of the candidate. Accepts country name or ISO code.'
                  type: string
                linkedinUrl:
                  description: 'The LinkedIn profile URL of the candidate. Must match `https?://(.*\.)?linkedin.com/...`.'
                  type: string
                  format: uri
                dateAvailable:
                  description: 'The available start date of the candidate. Format: `Y-m-d` (e.g. `2024-06-01`).'
                  type: string
                  format: date
                desiredSalary:
                  description: 'The desired salary of the candidate.'
                  type: string
                referredBy:
                  description: 'The person or entity that referred the candidate.'
                  type: string
                websiteUrl:
                  description: 'The personal website, blog, or online portfolio of the candidate. Must be a valid URL.'
                  type: string
                  format: uri
                highestEducation:
                  description: 'The highest completed education level of the candidate.'
                  type: string
                  enum: ['GED or Equivalent', 'High School', 'Some College', 'College - Associates', 'College - Bachelor of Arts', 'College - Bachelor of Fine Arts', 'College - Bachelor of Science', 'College - Master of Arts', 'College - Master of Fine Arts', 'College - Master of Science', 'College - Master of Business Administration', 'College - Doctorate', 'Medical Doctor', Other]
                collegeName:
                  description: 'The college or university of the candidate.'
                  type: string
                references:
                  description: 'A list of references supplied by the candidate.'
                  type: string
                resume:
                  description: 'Resume file for the candidate. Accepted MIME types: `application/pdf`, `application/msword`, `application/vnd.openxmlformats-officedocument.wordprocessingml.document`, `text/plain`, `application/rtf`, `image/jpeg`, `image/gif`, `image/png`, `image/tiff`, `image/bmp`.'
                  type: string
                  format: binary
                coverLetter:
                  description: 'Cover letter file for the candidate. Accepted MIME types: `application/pdf`, `application/msword`, `application/vnd.openxmlformats-officedocument.wordprocessingml.document`, `text/plain`, `application/rtf`, `image/jpeg`, `image/gif`, `image/png`, `image/tiff`, `image/bmp`.'
                  type: string
                  format: binary
              type: object
      responses:
        '200':
          description: 'Success. Returns the result status and the new candidate ID.'
          content:
            application/json:
              schema:
                title: CreateCandidateResponse
                properties:
                  result: { description: "Always 'success' on a successful request", type: string }
                  candidateId: { description: 'The ID of the newly created candidate', type: integer }
                type: object
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json: {  }
        '403':
          description: 'Insufficient user permissions or API access is not turned on.'
          content:
            application/json: {  }
        '404':
          description: 'Bad request url.'
          content:
            application/json: {  }
        '422':
          description: 'Unprocessable entity. One or more parameters failed validation.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - 'hiring:applications.write'
  /api/v1/applicant_tracking/job_opening:
    post:
      tags:
        - 'Public API'
        - 'Applicant Tracking'
      summary: 'Create Job Opening'
      description: 'Create a new job opening. The owner of the API key used must have access to ATS settings. Use the Get Company Locations and Get Hiring Leads endpoints to obtain valid IDs for `jobLocation` and `hiringLead`. On success, returns the new job opening ID.'
      operationId: create-job-opening
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              required:
                - employmentType
                - hiringLead
                - jobDescription
                - jobStatus
                - postingTitle
              properties:
                postingTitle:
                  description: 'The posting title of the job opening.'
                  type: string
                jobStatus:
                  description: 'The status of the job opening.'
                  type: string
                  enum: [Draft, Open, 'On Hold', Filled, Canceled]
                hiringLead:
                  description: 'The employee id (from the v1/applicant_tracking/hiring_leads endpoint) of the hiring lead for the job opening.'
                  type: integer
                department:
                  description: 'The department of the job opening.'
                  type: string
                employmentType:
                  description: 'The type of employment offered in the job opening, e.g. Full-Time, Part-Time, Contractor, etc.'
                  type: string
                minimumExperience:
                  description: 'The minimum experience level that qualifies a candidate for the job opening.'
                  type: string
                  enum: [Entry-level, Mid-level, Experienced, Manager/Supervisor, "Senior Manager/Supervisor'", Executive, 'Senior Executive']
                compensation:
                  description: 'The pay rate or compensation for the job opening.'
                  type: string
                jobLocation:
                  description: 'The location id (from the v1/applicant_tracking/locations endpoint) of the job opening. Omit this parameter for a remote job location.'
                  type: integer
                jobDescription:
                  description: 'The long-form text description of the job opening.'
                  type: string
                applicationQuestionResume:
                  description: 'Whether the job opening application has a standard question for resume (true) or not (false) or if uploading a resume is mandatory (required).'
                  type: string
                  enum: ['true', 'false', Required]
                applicationQuestionAddress:
                  description: 'Whether the job opening application has a standard question for address (true) or not (false) or if entering an address is mandatory (required).'
                  type: string
                  enum: ['true', 'false', Required]
                applicationQuestionLinkedinUrl:
                  description: 'Whether the job opening application has a standard question for LinkedIn profile url (true) or not (false) or if entering a LinkedIn profile url is mandatory (required).'
                  type: string
                  enum: ['true', 'false', Required]
                applicationQuestionDateAvailable:
                  description: 'Whether the job opening application has a standard question for availability date (true) or not (false) or if entering an availability date is mandatory (required).'
                  type: string
                  enum: ['true', 'false', Required]
                applicationQuestionDesiredSalary:
                  description: 'Whether the job opening application has a standard question for desired salary (true) or not (false) or if entering a desired salary is mandatory (required).'
                  type: string
                  enum: ['true', 'false', Required]
                applicationQuestionCoverLetter:
                  description: 'Whether the job opening application has a standard question for cover letter (true) or not (false) or if uploading a cover letter is mandatory (required).'
                  type: string
                  enum: ['true', 'false', Required]
                applicationQuestionReferredBy:
                  description: 'Whether the job opening application has a standard question for referred by (true) or not (false) or if entering referred by is mandatory (required).'
                  type: string
                  enum: ['true', 'false', Required]
                applicationQuestionWebsiteUrl:
                  description: 'Whether the job opening application has a standard question for website url (true) or not (false) or if entering a website url is mandatory (required).'
                  type: string
                  enum: ['true', 'false', Required]
                applicationQuestionHighestEducation:
                  description: 'Whether the job opening application has a standard question for highest education (true) or not (false) or if entering highest education is mandatory (required).'
                  type: string
                  enum: ['true', 'false', Required]
                applicationQuestionCollege:
                  description: 'Whether the job opening application has a standard question for college (true) or not (false) or if entering a college is mandatory (required).'
                  type: string
                  enum: ['true', 'false', Required]
                applicationQuestionReferences:
                  description: 'Whether the job opening application has a standard question for references (true) or not (false) or if entering references is mandatory (required).'
                  type: string
                  enum: ['true', 'false', Required]
                internalJobCode:
                  description: 'The internal job code for the job opening.'
                  type: string
                locationType:
                  description: 'The location type for the job opening. `0` = on-site (requires `jobLocation`), `1` = remote (no `jobLocation`), `2` = hybrid (requires `jobLocation`). Defaults to `1` if omitted and no `jobLocation` is provided, or `0` if `jobLocation` is provided.'
                  type: integer
                  enum: [0, 1, 2]
              type: object
      responses:
        '200':
          description: 'Success. Returns the result status and the new job opening ID.'
          content:
            application/json:
              schema:
                title: CreateJobOpeningResponse
                properties:
                  result: { description: "Always 'success' on a successful request", type: string }
                  jobOpeningId: { description: 'The ID of the newly created job opening', type: string }
                type: object
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json: {  }
        '403':
          description: 'Insufficient user permissions or API access is not turned on.'
          content:
            application/json: {  }
        '404':
          description: 'Bad request url.'
          content:
            application/json: {  }
        '422':
          description: 'Unprocessable entity. One or more parameters failed validation.'
          content:
            application/json: {  }
      security:
        -
          basic: []
  /api/v1/benefit/company_benefit:
    get:
      tags:
        - Benefits
        - 'Public API'
      summary: 'List Company Benefits'
      description: 'Returns all active (non-deleted) company benefit plans for the account. Each plan includes summary-level fields such as name, benefit category type, associated vendor and deduction IDs, effective date range, and catch-up eligibility flags. Deleted plans are excluded. To retrieve full detail for a specific plan (including SSO URL, description, and ACA fields), use "Get a company benefit".'
      operationId: list-company-benefits
      responses:
        '200':
          description: 'An object containing a companyBenefits array of benefit plan summaries.'
          content:
            application/json:
              schema:
                title: CompanyBenefitsListResponse
                properties:
                  companyBenefits: { description: 'List of company benefit plan summaries.', type: array, items: { $ref: '#/components/schemas/CompanyBenefitSummary' } }
                type: object
        '401':
          description: 'Unauthorized. Invalid or missing authentication credentials.'
        '500':
          description: 'Internal server error.'
      security:
        -
          basic: []
        -
          oauth:
            - benefit
  /api/v1/benefit/employee_benefit:
    get:
      tags:
        - Benefits
        - 'Public API'
      summary: 'List Employee Benefits'
      description: "Returns current and scheduled-future benefit enrollment records, grouped by employee. The response is a JSON object with a single `employeeBenefits` array where each entry contains the employee ID, the employee's current pay frequency (null when no pay schedule is set), and an `employeeBenefit` array of per-plan records that includes both the current enrollment record and any scheduled future-change records for that plan. Each record includes enrollment status, deduction date range, currency, occurrences-per-year, and the full employee/employer cost-sharing fields.\n\nA JSON request body with a `filters` object is required, and the `filters` object must contain at least one of `employeeId`, `companyBenefitId`, or `enrollmentStatusEffectiveDate`. Any combination is accepted. Providing no `filters` object or an empty `filters` object returns a 400 validation error. Filtering by `companyBenefitId` or `enrollmentStatusEffectiveDate` returns enrollments for every accessible employee, so the response can contain many entries. Use **List Company Benefits** (`list-company-benefits`) to look up valid `companyBenefitId` values.\n\nFuture-enrollment records are silently omitted when the authenticated user lacks permission to view scheduled benefit changes. Current enrollment records continue to be returned. An empty `employeeBenefit` array on a known employee can mean either no enrollments or that the caller cannot view them.\n\nNote: This endpoint accepts filters inside a JSON request body on a `GET` request, which is non-standard. Some HTTP clients, proxies, and gateways may strip request bodies from GET requests, which can produce confusing missing-filter behavior. Callers experiencing validation errors should verify that the request body is being preserved. A query-parameter-based endpoint will be released in the future."
      operationId: list-employee-benefits
      requestBody:
        description: 'Filters that scope the results. The `filters` object is required, and at least one of `employeeId`, `companyBenefitId`, or `enrollmentStatusEffectiveDate` must be provided. Any combination of the three is accepted.'
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EmployeeBenefitFilters'
      responses:
        '200':
          description: "An object containing an employeeBenefits array, each item representing one employee's benefit enrollments."
          content:
            application/json:
              schema:
                title: EmployeeBenefitsListResponse
                properties:
                  employeeBenefits: { description: 'List of per-employee benefit enrollment records.', type: array, items: { properties: { employeeId: { description: 'The ID of the employee.', type: integer }, payFrequency: { description: "The employee's current pay frequency (e.g., 'semi-monthly', 'biweekly'), or null if no pay schedule is set.", oneOf: [{ type: string }, { type: 'null' }] }, employeeBenefit: { description: "The employee's benefit plan enrollments, including both current and future scheduled changes.", type: array, items: { properties: { companyBenefitId: { description: 'The ID of the company benefit plan.', type: string }, companyBenefitName: { description: 'The name of the company benefit plan.', type: string }, coverageLevel: { description: 'The coverage level for this enrollment, or null if not applicable.', oneOf: [{ type: string }, { type: 'null' }] }, deductionEndDate: { description: 'The date deductions end for this enrollment (YYYY-MM-DD), or null.', oneOf: [{ type: string, format: date }, { type: 'null' }] }, deductionStartDate: { description: 'The date deductions begin for this enrollment (YYYY-MM-DD), or null.', oneOf: [{ type: string, format: date }, { type: 'null' }] }, enrollmentStatus: { description: 'The current or scheduled enrollment status.', type: string, enum: [Eligible, Enrolled, Waived, Withdrew, Terminated, Ineligible] }, enrollmentStatusEffectiveDate: { description: 'The date the enrollment status takes effect (YYYY-MM-DD).', type: string, format: date }, currencyCode: { description: 'ISO 4217 currency code for deduction amounts, or null if not applicable.', oneOf: [{ type: string }, { type: 'null' }] }, employeeAmount: { description: 'The employee-paid deduction amount.', oneOf: [{ type: number }, { type: 'null' }] }, employeeAmountType: { description: "The unit for employeeAmount (e.g., '%', '$').", oneOf: [{ type: string }, { type: 'null' }] }, employeePercentBasedOn: { description: "The basis for percent-based employee deductions (e.g., 'Gross'), or null.", oneOf: [{ type: string }, { type: 'null' }] }, employeeCapAmount: { description: 'The cap on employee deductions, or null if none.', oneOf: [{ type: number }, { type: 'null' }] }, employeeCapAmountType: { description: 'The unit for employeeCapAmount, or null.', oneOf: [{ type: string }, { type: 'null' }] }, employeeAnnualMax: { description: 'The annual maximum employee contribution, or null if none.', oneOf: [{ type: number }, { type: 'null' }] }, companyAmount: { description: 'The employer-paid contribution amount.', oneOf: [{ type: number }, { type: 'null' }] }, companyAmountType: { description: "The unit for companyAmount (e.g., '%', '$').", oneOf: [{ type: string }, { type: 'null' }] }, companyPercentBasedOn: { description: "The basis for percent-based employer contributions (e.g., 'Gross'), or null.", oneOf: [{ type: string }, { type: 'null' }] }, companyCapAmount: { description: 'The cap on employer contributions, or null if none.', oneOf: [{ type: number }, { type: 'null' }] }, companyCapAmountType: { description: 'The unit for companyCapAmount, or null.', oneOf: [{ type: string }, { type: 'null' }] }, companyAnnualMax: { description: 'The annual maximum employer contribution, or null if none.', oneOf: [{ type: number }, { type: 'null' }] }, occurrencesPerYear: { description: "The number of deduction occurrences per year based on the employee's pay schedule.", type: integer } }, type: object } } }, type: object } }
                type: object
        '400':
          description: 'Bad request. The request body is missing or not valid JSON, the `filters` object is missing, none of `employeeId`, `companyBenefitId`, or `enrollmentStatusEffectiveDate` was provided, or `enrollmentStatusEffectiveDate` is not in `YYYY-MM-DD` format.'
        '401':
          description: 'Unauthorized. Invalid or missing authentication credentials.'
        '403':
          description: 'Forbidden. The authenticated caller does not have permission to view benefit data.'
        '500':
          description: 'Internal server error.'
      security:
        -
          basic: []
        -
          oauth:
            - benefit
  /api/v1/benefit/member_benefit:
    get:
      tags:
        - Benefits
        - 'Public API'
      summary: 'List Member Benefit Events'
      description: 'Returns benefit enrollment events for all employees and their dependents over the past year, organized by member. Each entry identifies a member (employee or dependent) and lists their per-plan coverage events (eligibility granted, enrolled, or loss of coverage), sorted chronologically. Requires benefit settings access.'
      operationId: list-member-benefit-events
      responses:
        '200':
          description: 'An object containing a members array of member benefit event records.'
          content:
            application/json:
              schema:
                title: MemberBenefitEventsResponse
                properties:
                  members: { description: 'List of member benefit event records, one entry per employee or dependent.', type: array, items: { $ref: '#/components/schemas/MemberBenefitEvent' } }
                type: object
        '401':
          description: 'Unauthorized. Invalid or missing authentication credentials.'
        '403':
          description: 'Forbidden. The authenticated user does not have benefit settings access.'
      security:
        -
          basic: []
        -
          oauth:
            - benefit
  /api/v1/benefits/member-benefits:
    get:
      tags:
        - Benefits
        - 'Public API'
      summary: 'List Member Benefits'
      description: 'Returns a paginated list of benefit enrollment records for all members (employees and dependents) in the company for a given calendar year. Each record represents one member and includes the plans they held and the date ranges during which they held each enrollment status. Dependents appear alongside their subscribing employee via subscriberId. Use "List Company Benefits" to get valid planId values.'
      operationId: list-member-benefits
      parameters:
        -
          name: calendarYear
          in: query
          description: 'The 4-digit calendar year (YYYY) to retrieve benefit enrollment data for.'
          required: true
          schema:
            type: string
            pattern: '^\d{4}$'
            example: '2024'
        -
          name: page
          in: query
          description: 'The 1-based page number for pagination. The value is cast to an integer; values that cast to 0 or below are rejected with a 400. Defaults to 1.'
          required: false
          schema:
            type: string
            default: '1'
            example: '1'
        -
          name: pageSize
          in: query
          description: 'The number of items per page. The value is cast to an integer; values that cast to 0 or below, or to 100 or above, are rejected with a 400. Defaults to 25.'
          required: false
          schema:
            type: string
            default: '25'
            example: '25'
      responses:
        '200':
          description: 'A paginated list of member benefit enrollment records for the requested calendar year.'
          content:
            application/json:
              schema:
                title: MemberBenefitsGetSuccessResponse
                properties:
                  data: { type: array, items: { properties: { memberId: { description: 'The member identifier. Formatted as "employee.{id}" for employees and "dependent.{id}" for dependents.', type: string }, subscriberId: { description: 'The employee ID of the plan subscriber as a string. For dependent members, this is the ID of the employee they are enrolled under.', type: string }, plans: { type: array, items: { properties: { planId: { description: 'The benefit plan ID.', type: string }, dateRanges: { type: array, items: { properties: { startDate: { description: 'Start date of the enrollment period (YYYY-MM-DD).', type: string, format: date }, endDate: { description: 'End date of the enrollment period (YYYY-MM-DD), or null if the enrollment has no calculated end date.', oneOf: [{ type: string, format: date }, { type: 'null' }] }, status: { description: 'The enrollment status for this date range.', type: string, enum: [ENROLLED, WAIVED, TERMINATED, WITHDREW, ELIGIBLE, INELIGIBLE] } }, type: object } } }, type: object } } }, type: object } }
                  meta: { properties: { page: { description: 'The current page number.', type: integer }, pageSize: { description: 'The number of items per page.', type: integer }, totalPages: { description: 'Total number of pages available.', type: integer }, totalItems: { description: 'Total number of member records across all pages.', type: integer } }, type: object }
                  _links: { properties: { next: { properties: { href: { description: 'URL for the next page of results. Points to the current page when already on the last page.', type: string } }, type: object }, prev: { properties: { href: { description: 'URL for the previous page of results. Points to page 1 when already on the first page.', type: string } }, type: object } }, type: object }
                type: object
        '400':
          description: 'Validation error. Returned when calendarYear is missing or not a 4-digit year, or when page or pageSize are out of range.'
          content:
            application/json:
              schema:
                title: MemberBenefitsGetValidationErrorResponse
                properties:
                  code: { type: string, example: VALIDATION_ERROR }
                  message: { type: string, example: 'required property calendarYear is missing' }
                type: object
        '403':
          description: 'Permission denied. The authenticated user is not a benefit admin.'
          content:
            application/json:
              schema:
                title: MemberBenefitsGetPermissionDeniedResponse
                properties:
                  code: { type: string, example: PERMISSION_DENIED }
                  message: { type: string, example: 'Must be benefit admin user' }
                type: object
      security:
        -
          basic: []
        -
          oauth:
            - benefit
  /api/v1/benefits/settings/deduction_types/all:
    get:
      tags:
        - Benefits
        - 'Public API'
      summary: 'List Benefit Deduction Types'
      description: 'Returns all benefit deduction types available in the system. Each deduction type describes a category of payroll deduction (e.g. 401(k), HSA, Section 125) along with its allowable benefit plan types, default deduction code, and optional sub-types. Some deduction types are grouped under a parent with sub-types (e.g. Pre-Tax groups Health, Dental, etc.); in that case the parent entry has a non-empty `subTypes` array. Requires Benefits Administration permissions.'
      operationId: list-benefit-deduction-types
      responses:
        '200':
          description: 'An array of benefit deduction type objects.'
          content:
            application/json:
              schema:
                title: BenefitDeductionTypesResponse
                type: array
                items:
                  title: BenefitDeductionType
                  properties: { id: { description: 'The deduction type ID. Integer for leaf types (e.g. 1 for 401(k)); string slug for grouped parent types (e.g. "pretax_subtype").', oneOf: [{ type: integer }, { type: string }] }, deductionTypeName: { description: 'The display name of the deduction type.', type: string }, defaultDeductionCode: { description: 'The default payroll deduction code for this type.', type: string }, allowableBenefitTypes: { description: 'The benefit plan types this deduction type can be applied to (e.g. "health", "dental", "retirement").', type: array, items: { type: string } }, nonBenefitDeductionType: { description: 'Whether this is a non-benefit deduction type (e.g. garnishments).', type: boolean }, canBeCollectedByTrax: { description: 'Whether this deduction type can be collected via Trax Payroll.', type: boolean }, additionalDescription: { description: 'An optional additional description for display purposes.', type: string }, hideAnnualMax: { description: 'Whether the annual maximum field should be hidden for this deduction type.', type: boolean }, managedDeductionType: { description: 'The managed deduction type identifier, if applicable. Null if not managed.', oneOf: [{ type: string }, { type: 'null' }] }, subTypes: { description: 'Child deduction types grouped under this parent. Each entry has the same shape as a top-level deduction type. Empty array if this type has no sub-types.', type: array, items: { title: BenefitDeductionSubType, properties: { id: { description: 'The sub-type deduction type ID (always an integer).', type: integer }, deductionTypeName: { description: 'The display name of the sub-type.', type: string }, defaultDeductionCode: { description: 'The default payroll deduction code for this sub-type.', type: string }, allowableBenefitTypes: { description: 'Benefit plan types this sub-type applies to.', type: array, items: { type: string } }, nonBenefitDeductionType: { description: 'Whether this is a non-benefit deduction type.', type: boolean }, canBeCollectedByTrax: { description: 'Whether this sub-type can be collected via Trax Payroll.', type: boolean }, additionalDescription: { description: 'Additional description for display purposes.', type: string }, hideAnnualMax: { description: 'Whether the annual maximum field should be hidden.', type: boolean }, managedDeductionType: { description: 'The managed deduction type identifier, if applicable. Null if not managed.', oneOf: [{ type: string }, { type: 'null' }] }, subTypes: { description: 'Always an empty array for sub-types.', type: array, items: { type: object } }, subTypeText: { description: 'Sub-type selection label. Empty string for leaf types.', type: string }, deductionNote: { description: 'Informational note for this sub-type.', type: string }, deductionNoteLink: { description: 'URL for the deduction note link.', type: string }, deductionNoteLinkText: { description: 'Display text for the deduction note link.', type: string } }, type: object } }, subTypeText: { description: 'A label or question displayed alongside sub-type selection (e.g. "Reportable on the W-2?"). Empty string for types without sub-types.', type: string }, deductionNote: { description: 'An informational note to display to the user when configuring this deduction type.', type: string }, deductionNoteLink: { description: 'A URL for a "learn more" link associated with the deduction note.', type: string }, deductionNoteLinkText: { description: 'The display text for the deduction note link.', type: string } }
                  type: object
        '403':
          description: 'The authenticated user does not have Benefits Administration permissions.'
        '500':
          description: 'An unexpected server error occurred.'
      security:
        -
          basic: []
        -
          oauth:
            - benefit
  /api/v1/company-profile-data/industry-codes:
    put:
      tags:
        - 'Public API'
        - Company
      summary: 'Update Company Industry Codes'
      description: 'Updates the industry codes associated with a company.'
      operationId: put-company-industry-codes
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - industryIds
              properties:
                industryIds:
                  description: 'List of industry IDs associated with the company. Currently limited to a single entry.'
                  type: array
                  items: { type: integer }
              type: object
            example:
              industryIds:
                - 1
      responses:
        '200':
          description: 'Returns the updated list of enabled industries for the company'
          content:
            application/json:
              schema:
                title: UpdateCompanyIndustryCodesResponse
                properties:
                  company_industries: { $ref: '#/components/schemas/CompanyIndustryCollection', description: 'List of enabled industries for the company' }
                type: object
                example:
                  company_industries: [{ industryId: 4, addedByCustomer: false, addedByUserId: 1, addedYmdt: '2021-09-01T12:00:00+00:00' }]
        '400':
          description: 'Bad request due to invalid request structure or multiple industry IDs provided'
          content:
            application/json:
              schema:
                title: UpdateCompanyIndustryCodes400Response
                properties:
                  message: { type: string }
                type: object
                example:
                  message: 'Multiple industries per-company are not supported at this time.'
        '401':
          description: 'Missing or invalid authentication'
        '403':
          description: 'Insufficient permissions or release toggle not enabled'
          content:
            application/json:
              schema:
                title: UpdateCompanyIndustryCodes403Response
                properties:
                  message: { type: string }
                type: object
                example:
                  message: 'You do not have permission to access this resource.'
        '404':
          description: 'Industry ID not found'
          content:
            application/json:
              schema:
                title: UpdateCompanyIndustryCodes404Response
                properties:
                  message: { type: string }
                type: object
                example:
                  message: 'The requested industry ID does not exist.'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                title: UpdateCompanyIndustryCodes500Response
                properties:
                  message: { type: string }
                type: object
                example:
                  message: 'An error occurred while processing your request.'
      security:
        -
          oauth:
            - 'company:details.write'
  /api/v1/company_information:
    get:
      tags:
        - Employees
        - 'Public API'
      summary: 'Get Company Information'
      description: "Returns basic profile information for the company, including its legal name, display name, primary address, and contact phone number. For companies using BambooHR Payroll, the legal name and address are sourced from the active payroll client metadata; for all other companies, the data comes from the company's account settings."
      operationId: get-company-information
      responses:
        '200':
          description: 'Company information retrieved successfully.'
          content:
            application/json:
              schema:
                title: CompanyInformation
                properties:
                  legalName: { description: 'The legal name of the company.', type: string }
                  displayName: { description: 'The display name of the company.', type: string }
                  address: { description: 'The primary address of the company.', properties: { line1: { description: 'The first line of the address.', type: string }, line2: { description: 'The second line of the address. May be an empty string.', type: string }, city: { description: 'The city.', type: string }, state: { description: 'The state or province abbreviation.', type: string }, zip: { description: 'The ZIP or postal code.', type: string } }, type: object }
                  phone: { description: 'The primary contact phone number of the company.', type: string }
                type: object
                example:
                  legalName: BambooHR
                  displayName: BambooHR
                  address: { line1: '335 S 560 W', line2: 'Suite 200', city: Lindon, state: UT, zip: '84042' }
                  phone: 801-724-6600
        '403':
          description: 'The API user does not have permission to access company information.'
      security:
        -
          basic: []
        -
          oauth:
            - 'company:info'
  /api/v1/company-profile-integrations:
    get:
      tags:
        - 'Company Profile'
        - 'Public API'
      summary: 'Get Company Profile Integrations'
      description: "Returns the list of integration feature identifiers currently enabled for the company. Each identifier is an uppercase string key (e.g. `BAMBOOHR_PAYROLL`, `TIME_TRACKING`, `E_SIGNATURES`) representing a product feature or integration that has been activated on the account. The list reflects the company's current subscription and configuration."
      operationId: get-company-profile-integrations
      responses:
        '200':
          description: 'List of enabled integration identifiers for the company.'
          content:
            application/json:
              schema:
                title: CompanyProfileIntegrations
                properties:
                  integrations: { description: 'List of installed integration identifiers.', type: array, items: { type: string } }
                type: object
                example:
                  integrations: [BAMBOOHR_PAYROLL, GOOGLE_SSO, SLACK]
        '403':
          description: 'The API user does not have permission to access company profile integrations.'
        '500':
          description: 'An unexpected error occurred while retrieving company profile integrations.'
      security:
        -
          basic: []
        -
          oauth:
            - 'company:details'
  /api/v1/company-profile-data/company-information:
    patch:
      tags:
        - 'Company Profile'
        - 'Public API'
      summary: 'Update company information (phone, address, legal name)'
      description: 'Updates legal name, phone, and/or address for the company (application/merge-patch+json). String fields must be JSON strings (not numbers). Response matches GET /api/v1/company-profile-data.'
      operationId: patch-company-profile-company-information
      requestBody:
        required: true
        content:
          application/merge-patch+json:
            schema:
              properties:
                legalName:
                  description: 'Legal name of the company (name_of_employer). Required (non-empty) when this field is present in the patch.'
                  type: string
                phone:
                  description: 'Contact phone number. Required (non-empty) when this field is present in the patch.'
                  type: string
                address:
                  description: 'Company address. When `address` is included in the patch (non-null object), `city`, `state`, and `zip` must all be sent as non-empty strings. `line1` and `line2` are optional (JSON null removes a line per merge-patch).'
                  properties: { line1: { description: 'Street address line 1 (employerAddress). Optional; JSON null removes the value per merge-patch semantics.', oneOf: [{ type: string }, { type: 'null' }] }, line2: { description: 'Street address line 2 (employerAddress). Optional; JSON null removes the value per merge-patch semantics.', oneOf: [{ type: string }, { type: 'null' }] }, city: { description: 'City. Required (non-empty) when address is patched.', type: string }, state: { description: 'State. Required (non-empty) when address is patched.', type: string }, zip: { description: 'Zip or postal code. Required (non-empty) when address is patched.', type: string } }
                  type: object
              type: object
      responses:
        '200':
          description: 'Successful response; same shape as GET company-profile-data'
          content:
            application/json:
              schema:
                title: CompanyProfileData
                properties:
                  id: { type: integer }
                  subdomain: { oneOf: [{ type: string }, { type: 'null' }] }
                  legalName: { oneOf: [{ type: string }, { type: 'null' }] }
                  displayName: { oneOf: [{ type: string }, { type: 'null' }] }
                  address: { properties: { line1: { oneOf: [{ type: string }, { type: 'null' }] }, line2: { oneOf: [{ type: string }, { type: 'null' }] }, city: { oneOf: [{ type: string }, { type: 'null' }] }, state: { oneOf: [{ type: string }, { type: 'null' }] }, zip: { oneOf: [{ type: string }, { type: 'null' }] } }, type: object }
                  phone: { oneOf: [{ type: string }, { type: 'null' }] }
                  status: { type: string }
                  terminationDate: { oneOf: [{ type: string }, { type: 'null' }] }
                  industryCode: { oneOf: [{ type: string }, { type: 'null' }] }
                type: object
        '400':
          description: 'Invalid JSON or Content-Type'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Permission denied'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Validation failed (invalid field types or incomplete profile after merge)'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - 'company:details.write'
  /api/v1/company-profile-data/display-name:
    put:
      tags:
        - 'Company Profile'
        - 'Public API'
      summary: 'Update company display name'
      description: 'Updates the company display name. Requires admin permissions and the company:details.write OAuth scope. The display name must be a non-empty string with a maximum length of 255 characters. Upon successful update, the system logs the change and broadcasts an event to notify other services.'
      operationId: put-company-profile-display-name
      requestBody:
        content:
          application/json:
            schema:
              required:
                - companyName
              properties:
                companyName:
                  description: 'The new display name for the company. Must be a non-empty string with a maximum length of 255 characters (character count, not bytes, so multibyte UTF-8 characters are properly supported).'
                  type: string
                  example: 'Acme Corporation'
              type: object
      responses:
        '200':
          description: 'Company name updated successfully'
          content:
            application/json:
              schema:
                title: UpdateCompanyNameSuccessResponse
                properties:
                  success: { description: 'Indicates whether the operation was successful', type: boolean, example: true }
                type: object
        '400':
          description: 'Invalid request body or company name'
          content:
            application/json:
              schema:
                title: UpdateCompanyNameBadRequestResponse
                properties:
                  error: { properties: { message: { type: string } }, type: object }
                type: object
        '403':
          description: 'Permission denied'
          content:
            application/json:
              schema:
                title: UpdateCompanyNameForbiddenResponse
                properties:
                  error: { properties: { message: { type: string } }, type: object }
                type: object
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                title: UpdateCompanyNameInternalErrorResponse
                properties:
                  error: { properties: { message: { type: string } }, type: object }
                type: object
      security:
        -
          oauth:
            - 'company:details.write'
  /api/v1/compensation/benchmarks:
    get:
      tags:
        - 'Compensation Benchmarking'
        - 'Public API'
      summary: 'List Compensation Benchmarks'
      description: "Returns every job/location pair the company tracks for compensation benchmarking. Each entry includes job and location identifiers, the benchmarks attached to that pair, the employees currently assigned to that job and location, and the company's configured internal pay band if one exists. Use the returned `jobDetails.id` and `locationDetails.id` values as the `jobId` and `locationId` inputs for `GET /api/v1/compensation/benchmarks/details`. Top-level `dismiss*Banner` fields reflect UI banner state and are not part of the benchmark data contract."
      operationId: list-compensation-benchmarks
      responses:
        '200':
          description: 'Object with a `jobLocationPairs` array and banner-dismissal flags.'
          content:
            application/json:
              schema:
                title: CompensationBenchmarksList
                properties:
                  jobLocationPairs: { type: array, items: { title: CompensationBenchmarkJobLocationPair, properties: { jobDetails: { properties: { id: { type: string }, title: { type: string } }, type: object }, locationDetails: { properties: { id: { type: string }, name: { type: string }, location: { type: string }, country: { oneOf: [{ type: string }, { type: 'null' }] } }, oneOf: [{ type: object }, { type: 'null' }] }, isRemote: { type: boolean }, benchmarks: { description: 'Saved benchmarks attached to this job/location. Empty when none are configured.', type: array, items: { title: CompensationBenchmarkOverview, properties: { id: { type: string }, values: { properties: { median: { type: number }, min: { type: number }, max: { type: number }, currencyCode: { type: string }, originalMedian: { oneOf: [{ type: number }, { type: 'null' }] }, originalMin: { oneOf: [{ type: number }, { type: 'null' }] }, originalMax: { oneOf: [{ type: number }, { type: 'null' }] }, originalCurrencyCode: { oneOf: [{ type: string }, { type: 'null' }] } }, type: object }, sourceId: { oneOf: [{ type: string }, { type: 'null' }] }, sourceColor: { oneOf: [{ type: string }, { type: 'null' }] }, isMercer: { type: boolean }, dataYear: { description: 'Mercer data year; defaults to the current Mercer data year when missing on a Mercer benchmark. `null` for non-Mercer benchmarks.', oneOf: [{ type: string }, { type: 'null' }] } }, type: object } }, employees: { type: array, items: { title: CompensationBenchmarkJobLocationEmployee, properties: { id: { type: string }, name: { type: string }, salary: { properties: { amount: { type: number }, currencyCode: { type: string } }, type: object }, photoUrl: { type: string }, managerName: { oneOf: [{ type: string }, { type: 'null' }] }, managerTitle: { oneOf: [{ type: string }, { type: 'null' }] }, division: { properties: { id: { type: string }, value: { type: string } }, oneOf: [{ type: object }, { type: 'null' }] }, department: { properties: { id: { type: string }, value: { type: string } }, oneOf: [{ type: object }, { type: 'null' }] }, currencyConversionFailed: { type: boolean } }, type: object } }, internalJobPayBand: { properties: { jobId: { type: string }, group: { type: string }, level: { type: string }, values: { properties: { median: { type: number }, min: { type: number }, max: { type: number }, currencyCode: { type: string }, originalMedian: { oneOf: [{ type: number }, { type: 'null' }] }, originalMin: { oneOf: [{ type: number }, { type: 'null' }] }, originalMax: { oneOf: [{ type: number }, { type: 'null' }] }, originalCurrencyCode: { oneOf: [{ type: string }, { type: 'null' }] }, description: { type: string } }, type: object } }, oneOf: [{ type: object }, { type: 'null' }] } }, type: object } }
                  dismissReviewBanner: { type: boolean }
                  dismissAddLocationBanner: { type: boolean }
                  dismissMercerDataInfoBanner: { type: boolean }
                type: object
        '403':
          description: 'The authenticated caller lacks permission to read compensation benchmarks.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Unexpected server error while loading compensation benchmarks.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - compensation_benchmarks
    put:
      tags:
        - 'Compensation Benchmarking'
        - 'Public API'
      summary: 'Update Compensation Benchmark'
      description: 'Updates an existing compensation benchmark identified by `id`. The `id` of an existing benchmark can be obtained from `GET /api/v1/compensation/benchmarks/details`. Request fields that are omitted or `null` are cleared on the stored benchmark; callers should send the full benchmark payload to preserve values. On success, returns the saved benchmark row wrapped in `savedBenchmark` together with a status `message`.'
      operationId: update-compensation-benchmark
      requestBody:
        content:
          application/json:
            schema:
              title: UpdateCompensationBenchmarkRequest
              required:
                - id
                - currencyCode
                - benchmarkValue
                - benchmarkMin
                - benchmarkMax
              properties:
                id:
                  description: 'ID of the benchmark to update.'
                  type: string
                currencyCode:
                  description: 'ISO 4217 currency code for the benchmark values.'
                  type: string
                mjlJobCode:
                  description: 'Mercer Job Library code associated with this benchmark. Stored on the benchmark as `mercerJobCode`.'
                  type: string
                benchmarkValue:
                  description: 'Benchmark median value.'
                  type: number
                benchmarkMin:
                  description: 'Benchmark minimum value.'
                  type: number
                benchmarkMax:
                  description: 'Benchmark maximum value.'
                  type: number
                benchmarkSource:
                  description: 'Free-text label describing where the benchmark came from.'
                  type: string
                externalJobTitle:
                  type: string
                externalLocation:
                  type: string
                externalLevel:
                  type: string
                externalJobDescription:
                  type: string
                companiesSurveyed:
                  type: integer
                employeesSurveyed:
                  type: integer
                sourceId:
                  description: 'ID of the benchmark source from `GET /api/v1/compensation/benchmarks/sources`.'
                  type: string
                sourceDate:
                  description: 'Date the benchmark source data applies to.'
                  type: string
                dataYear:
                  description: 'Year of the underlying survey data. Omitting this field clears `data_year` on the stored benchmark, so callers updating a Mercer benchmark should resend the existing value.'
                  type: string
                externalCountry:
                  type: string
                externalSecondaryLocation:
                  type: string
                externalCompanySize:
                  type: string
                externalIndustry:
                  type: string
              type: object
      responses:
        '200':
          description: 'Object containing the saved benchmark row and a status message.'
          content:
            application/json:
              schema:
                title: UpdatedCompensationBenchmark
                properties:
                  savedBenchmark: { properties: { id: { type: string }, jobTitleId: { type: string }, jobLocationId: { oneOf: [{ type: string }, { type: 'null' }] }, mercerJobCode: { oneOf: [{ type: string }, { type: 'null' }] }, benchmarkSource: { oneOf: [{ type: string }, { type: 'null' }] }, currencyCode: { oneOf: [{ type: string }, { type: 'null' }] }, benchmarkValue: { oneOf: [{ type: number }, { type: 'null' }] }, benchmarkMin: { oneOf: [{ type: number }, { type: 'null' }] }, benchmarkMax: { oneOf: [{ type: number }, { type: 'null' }] }, externalJobTitle: { oneOf: [{ type: string }, { type: 'null' }] }, externalLocation: { oneOf: [{ type: string }, { type: 'null' }] }, externalLevel: { oneOf: [{ type: string }, { type: 'null' }] }, externalJobDescription: { oneOf: [{ type: string }, { type: 'null' }] }, companiesSurveyed: { oneOf: [{ type: integer }, { type: 'null' }] }, employeesSurveyed: { oneOf: [{ type: integer }, { type: 'null' }] }, sourceId: { oneOf: [{ type: string }, { type: 'null' }] }, sourceDate: { oneOf: [{ type: string }, { type: 'null' }] }, dataYear: { oneOf: [{ type: string }, { type: 'null' }] }, externalCountry: { oneOf: [{ type: string }, { type: 'null' }] }, externalSecondaryLocation: { oneOf: [{ type: string }, { type: 'null' }] }, externalCompanySize: { oneOf: [{ type: string }, { type: 'null' }] }, externalIndustry: { oneOf: [{ type: string }, { type: 'null' }] } }, type: object }
                  message: { type: string }
                type: object
        '403':
          description: 'The authenticated caller lacks permission to modify compensation benchmarks.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Unexpected server error while saving the benchmark.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - compensation_benchmarks.write
    post:
      tags:
        - 'Compensation Benchmarking'
        - 'Public API'
      summary: 'Create Compensation Benchmark'
      description: "Creates a new compensation benchmark for a specific company job title (and optionally a specific job location). The `jobTitleId` value comes from `GET /api/v1/compensation/benchmarks` (`jobDetails.id`) or the company's job-title list. When `jobLocationId` is omitted, the benchmark applies to the job title at any location. Returns the saved benchmark wrapped in `savedBenchmark` along with a status `message`."
      operationId: create-compensation-benchmark
      requestBody:
        content:
          application/json:
            schema:
              title: CreateCompensationBenchmarkRequest
              required:
                - jobTitleId
                - currencyCode
                - benchmarkValue
                - benchmarkMin
                - benchmarkMax
              properties:
                jobTitleId:
                  description: 'ID of the company job title the benchmark applies to.'
                  type: string
                jobLocationId:
                  description: 'Optional job location ID to scope the benchmark to a specific location.'
                  type: string
                currencyCode:
                  description: 'ISO 4217 currency code for the benchmark values.'
                  type: string
                mjlJobCode:
                  description: 'Mercer Job Library code associated with this benchmark. Stored on the benchmark as `mercerJobCode`.'
                  type: string
                benchmarkValue:
                  description: 'Benchmark median value.'
                  type: number
                benchmarkMin:
                  description: 'Benchmark minimum value.'
                  type: number
                benchmarkMax:
                  description: 'Benchmark maximum value.'
                  type: number
                benchmarkSource:
                  description: 'Free-text label describing where the benchmark came from.'
                  type: string
                externalJobTitle:
                  type: string
                externalLocation:
                  type: string
                externalLevel:
                  type: string
                externalJobDescription:
                  type: string
                companiesSurveyed:
                  type: integer
                employeesSurveyed:
                  type: integer
                sourceId:
                  description: 'ID of the benchmark source from `GET /api/v1/compensation/benchmarks/sources`.'
                  type: string
                sourceDate:
                  description: 'Date the benchmark source data applies to.'
                  type: string
                dataYear:
                  description: 'Year of the underlying survey data.'
                  type: string
                externalCountry:
                  type: string
                externalSecondaryLocation:
                  type: string
                externalCompanySize:
                  type: string
                externalIndustry:
                  type: string
              type: object
      responses:
        '200':
          description: 'Object containing the newly created benchmark row and a status message.'
          content:
            application/json:
              schema:
                title: CreatedCompensationBenchmark
                properties:
                  savedBenchmark: { properties: { id: { type: string }, jobTitleId: { type: string }, jobLocationId: { oneOf: [{ type: string }, { type: 'null' }] }, mercerJobCode: { oneOf: [{ type: string }, { type: 'null' }] }, benchmarkSource: { oneOf: [{ type: string }, { type: 'null' }] }, currencyCode: { oneOf: [{ type: string }, { type: 'null' }] }, benchmarkValue: { oneOf: [{ type: number }, { type: 'null' }] }, benchmarkMin: { oneOf: [{ type: number }, { type: 'null' }] }, benchmarkMax: { oneOf: [{ type: number }, { type: 'null' }] }, externalJobTitle: { oneOf: [{ type: string }, { type: 'null' }] }, externalLocation: { oneOf: [{ type: string }, { type: 'null' }] }, externalLevel: { oneOf: [{ type: string }, { type: 'null' }] }, externalJobDescription: { oneOf: [{ type: string }, { type: 'null' }] }, companiesSurveyed: { oneOf: [{ type: integer }, { type: 'null' }] }, employeesSurveyed: { oneOf: [{ type: integer }, { type: 'null' }] }, sourceId: { oneOf: [{ type: string }, { type: 'null' }] }, sourceDate: { oneOf: [{ type: string }, { type: 'null' }] }, dataYear: { oneOf: [{ type: string }, { type: 'null' }] }, externalCountry: { oneOf: [{ type: string }, { type: 'null' }] }, externalSecondaryLocation: { oneOf: [{ type: string }, { type: 'null' }] }, externalCompanySize: { oneOf: [{ type: string }, { type: 'null' }] }, externalIndustry: { oneOf: [{ type: string }, { type: 'null' }] } }, type: object }
                  message: { type: string }
                type: object
        '403':
          description: 'The authenticated caller lacks permission to create compensation benchmarks.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Unexpected server error while creating the benchmark.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - compensation_benchmarks.write
  /api/v1/compensation/benchmarks/details/export:
    get:
      tags:
        - 'Compensation Benchmarking'
        - 'Public API'
      summary: 'Export Compensation Benchmark Details'
      description: "Returns a CSV export of the compensation benchmark detail view for a single job, optionally scoped to a specific location. Rows include employee-level data (name, location, annualized pay, compa-ratio, range penetration, years of experience) alongside the company's internal pay band for the job. Use `GET /api/v1/compensation/benchmarks` to find valid `jobId` and `locationId` values. When `locationId` is omitted, the export aggregates across all locations for the job."
      operationId: export-compensation-benchmark-details
      parameters:
        -
          name: jobId
          in: query
          description: 'ID of the company job title to export benchmark details for.'
          required: true
          schema:
            type: string
        -
          name: locationId
          in: query
          description: 'Optional job location ID to restrict the export to one location.'
          required: false
          schema:
            type: string
      responses:
        '200':
          description: 'CSV payload streamed with a `Content-Disposition: attachment` header. The filename is derived from the job title and location.'
          content:
            text/csv:
              schema:
                title: CompensationBenchmarkDetailsExport
                type: string
        '403':
          description: 'The authenticated caller lacks permission to read compensation benchmarks.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Unexpected server error while generating the CSV export. Also returned when `jobId` is missing.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - compensation_benchmarks
  /api/v1/compensation/benchmarks/details:
    get:
      tags:
        - 'Compensation Benchmarking'
        - 'Public API'
      summary: 'Get Compensation Benchmark Details'
      description: 'Returns detailed benchmark data for a single company job title, optionally scoped to a specific location. The response includes the company pay range derived from current employees, the internal pay band configured for the job (if any), Mercer benchmark details when a Mercer benchmark is linked, every saved benchmark for the job/location (empty when none exist), and per-employee salary and compensation stats. Use `GET /api/v1/compensation/benchmarks` to find valid `jobId` and `locationId` values.'
      operationId: get-compensation-benchmark-details
      parameters:
        -
          name: jobId
          in: query
          description: 'ID of the company job title to fetch benchmark details for.'
          required: true
          schema:
            type: string
        -
          name: locationId
          in: query
          description: 'Optional job location ID. When omitted, results aggregate across all locations for the job.'
          required: false
          schema:
            type: string
      responses:
        '200':
          description: 'Benchmark detail object for the requested job (and optional location).'
          content:
            application/json:
              schema:
                title: CompensationBenchmarkDetails
                properties:
                  jobId: { type: string }
                  jobTitle: { type: string }
                  locationId: { oneOf: [{ type: string }, { type: 'null' }] }
                  location: { oneOf: [{ type: string }, { type: 'null' }] }
                  mercerBenchmarkDetails: { description: 'Populated when a Mercer benchmark is linked to this job/location.', properties: { id: { type: string }, source: { type: string }, jobCode: { type: string }, jobTitle: { type: string }, jobLocation: { type: string }, jobLevel: { oneOf: [{ type: string }, { type: 'null' }] }, jobDescription: { oneOf: [{ type: string }, { type: 'null' }] }, formattedJobDescription: { oneOf: [{ type: array, items: { type: string } }, { type: 'null' }] }, companiesSurveyed: { oneOf: [{ type: integer }, { type: 'null' }] }, employeesSurveyed: { oneOf: [{ type: integer }, { type: 'null' }] }, lastReviewed: { oneOf: [{ type: string }, { type: 'null' }] }, values: { properties: { median: { type: number }, min: { type: number }, max: { type: number }, currencyCode: { type: string }, originalMedian: { oneOf: [{ type: number }, { type: 'null' }] }, originalMin: { oneOf: [{ type: number }, { type: 'null' }] }, originalMax: { oneOf: [{ type: number }, { type: 'null' }] }, originalCurrencyCode: { oneOf: [{ type: string }, { type: 'null' }] }, description: { oneOf: [{ type: string }, { type: 'null' }] } }, type: object }, createdYmdt: { oneOf: [{ type: string }, { type: 'null' }] }, updatedYmdt: { oneOf: [{ type: string }, { type: 'null' }] }, dataYear: { oneOf: [{ type: string }, { type: 'null' }] } }, oneOf: [{ type: object }, { type: 'null' }] }
                  companyPayRange: { description: 'Pay range aggregated from the current employees in this job and location.', properties: { median: { type: number }, min: { type: number }, max: { type: number }, currencyCode: { type: string }, originalMedian: { oneOf: [{ type: number }, { type: 'null' }] }, originalMin: { oneOf: [{ type: number }, { type: 'null' }] }, originalMax: { oneOf: [{ type: number }, { type: 'null' }] }, originalCurrencyCode: { oneOf: [{ type: string }, { type: 'null' }] } }, oneOf: [{ type: object }, { type: 'null' }] }
                  internalJobPayBand: { description: 'Company-configured internal pay band for this job.', properties: { median: { type: number }, min: { type: number }, max: { type: number }, currencyCode: { type: string }, originalMedian: { oneOf: [{ type: number }, { type: 'null' }] }, originalMin: { oneOf: [{ type: number }, { type: 'null' }] }, originalMax: { oneOf: [{ type: number }, { type: 'null' }] }, originalCurrencyCode: { oneOf: [{ type: string }, { type: 'null' }] }, description: { oneOf: [{ type: string }, { type: 'null' }] } }, oneOf: [{ type: object }, { type: 'null' }] }
                  benchmarkValues: { description: 'All saved benchmarks for this job/location. Empty when no benchmarks exist.', type: array, items: { properties: { id: { type: integer }, jobTitle: { oneOf: [{ type: string }, { type: 'null' }] }, lastEdited: { oneOf: [{ type: string }, { type: 'null' }] }, sourceColorCode: { oneOf: [{ type: string }, { type: 'null' }] }, sourceDate: { oneOf: [{ type: string }, { type: 'null' }] }, createdYmdt: { oneOf: [{ type: string }, { type: 'null' }] }, updatedYmdt: { oneOf: [{ type: string }, { type: 'null' }] }, dataYear: { oneOf: [{ type: string }, { type: 'null' }] }, median: { type: number }, min: { type: number }, max: { type: number }, currencyCode: { type: string } }, type: object } }
                  employees: { type: array, items: { title: CompensationBenchmarkDetailEmployee, properties: { id: { type: integer }, name: { type: string }, jobTitle: { properties: { id: { type: string }, value: { type: string }, jobTitle: { type: string } }, type: object }, location: { properties: { id: { type: string }, value: { type: string }, name: { type: string } }, oneOf: [{ type: object }, { type: 'null' }] }, salary: { properties: { amount: { type: number }, currencyCode: { type: string } }, type: object }, varianceFromPayBand: { properties: { amount: { type: number }, currencyCode: { type: string } }, oneOf: [{ type: object }, { type: 'null' }] }, yearsAtCompany: { type: integer }, rangePenetration: { oneOf: [{ type: number }, { type: 'null' }] }, compaRatio: { oneOf: [{ type: number }, { type: 'null' }] }, compaRatioStatus: { oneOf: [{ type: string }, { type: 'null' }] }, photoUrl: { type: string }, country: { oneOf: [{ type: string }, { type: 'null' }] }, isRemote: { oneOf: [{ type: boolean }, { type: 'null' }] }, currencyConversionFailed: { type: boolean } }, type: object } }
                type: object
        '403':
          description: 'The authenticated caller lacks permission to read compensation benchmarks.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Unexpected server error while loading benchmark details. Also returned when `jobId` is missing.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - compensation_benchmarks
  '/api/v1/compensation/benchmarks/{id}':
    delete:
      tags:
        - 'Compensation Benchmarking'
        - 'Public API'
      summary: 'Delete Compensation Benchmark'
      description: 'Permanently removes the compensation benchmark identified by `id`. The `id` is a numeric benchmark row identifier; non-numeric values are rejected by the handler. Valid values are returned by `GET /api/v1/compensation/benchmarks/details` under `benchmarkValues[].id` and by the create/update endpoints as `savedBenchmark.id`.'
      operationId: delete-compensation-benchmark
      parameters:
        -
          name: id
          in: path
          description: 'Integer ID of the compensation benchmark to delete.'
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Literal string `"Success"` indicating the benchmark was deleted.'
          content:
            application/json:
              schema:
                title: DeleteCompensationBenchmarkResponse
                type: string
        '403':
          description: 'The authenticated caller lacks permission to delete compensation benchmarks.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Unexpected server error while deleting the benchmark.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - compensation_benchmarks.write
  /api/v1/compensation/benchmarks/sources:
    get:
      tags:
        - 'Compensation Benchmarking'
        - 'Public API'
      summary: 'List Compensation Benchmark Sources'
      description: 'Returns every enabled benchmark source configured for the company. Each source identifies where a benchmark value came from (for example, a specific survey provider) and includes a display color and the count of benchmarks currently attached to that source. Use the returned `id` as the `sourceId` input when creating or updating a benchmark.'
      operationId: list-compensation-benchmark-sources
      responses:
        '200':
          description: 'Array of benchmark sources. Empty when no sources are configured.'
          content:
            application/json:
              schema:
                title: CompensationBenchmarkSources
                type: array
                items:
                  title: CompensationBenchmarkSource
                  properties: { id: { type: string }, name: { type: string }, sort: { description: 'Sort order position, returned as a numeric string.', type: string }, count: { description: 'Number of benchmarks attached to this source, returned as a numeric string.', type: string }, colorCode: { description: 'Hex color used to display the source in the UI.', oneOf: [{ type: string }, { type: 'null' }] } }
                  type: object
        '403':
          description: 'The authenticated caller lacks permission to read compensation benchmarks.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Unexpected server error while loading benchmark sources.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - compensation_benchmarks
    put:
      tags:
        - 'Compensation Benchmarking'
        - 'Public API'
      summary: 'Update Compensation Benchmark Sources'
      description: 'Updates the name and sort order of one or more existing benchmark sources in a single call. Every item in `benchmarkSources` must include a non-empty `id` and `name`; any item with the reserved name `mercer` (case-insensitive) is rejected because Mercer sources are managed separately. Use `GET /api/v1/compensation/benchmarks/sources` to obtain the current `id` values. Returns `{ "result": "success" }` when all updates are applied.'
      operationId: update-compensation-benchmark-sources
      requestBody:
        content:
          application/json:
            schema:
              title: UpdateCompensationBenchmarkSourcesRequest
              required:
                - benchmarkSources
              properties:
                benchmarkSources:
                  type: array
                  items: { title: UpdateCompensationBenchmarkSourceItem, required: [id, name], properties: { id: { description: 'ID of the existing benchmark source to update.', type: string }, name: { description: 'New display name for the source. Cannot equal `mercer`.', type: string }, sort: { description: 'Optional new sort position, as a numeric string.', type: string } }, type: object }
              type: object
      responses:
        '200':
          description: 'Object containing `{ "result": "success" }` when all source updates succeed.'
          content:
            application/json:
              schema:
                title: UpdateCompensationBenchmarkSourcesResponse
                properties:
                  result: { type: string }
                type: object
        '400':
          description: 'The `benchmarkSources` array is missing, empty, contains items without `id` or `name`, or contains the reserved `mercer` name.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'The authenticated caller lacks permission to modify compensation benchmarks.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Unexpected server error while updating benchmark sources.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - compensation_benchmarks.write
    post:
      tags:
        - 'Compensation Benchmarking'
        - 'Public API'
      summary: 'Create Compensation Benchmark Source'
      description: "Creates a new benchmark source the company can attach to its benchmarks. The `name` must be non-empty; the reserved name `mercer` (case-insensitive) is rejected because Mercer sources are managed separately. Returns the new source's ID and the trimmed name."
      operationId: create-compensation-benchmark-source
      requestBody:
        content:
          application/json:
            schema:
              title: CreateCompensationBenchmarkSourceRequest
              required:
                - name
              properties:
                name:
                  description: 'Display name for the new benchmark source. Cannot be empty or equal to `mercer`.'
                  type: string
                colorCode:
                  description: 'Optional hex color used to display the source in the UI.'
                  type: string
              type: object
      responses:
        '200':
          description: "Object with the newly created source's ID and name."
          content:
            application/json:
              schema:
                title: CreatedCompensationBenchmarkSource
                properties:
                  id: { description: 'ID of the newly created benchmark source.', type: integer }
                  name: { type: string }
                type: object
        '400':
          description: 'The `name` is missing, empty, or the reserved value `mercer`.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'The authenticated caller lacks permission to modify compensation benchmarks.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Unexpected server error while creating the benchmark source.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - compensation_benchmarks.write
    delete:
      tags:
        - 'Compensation Benchmarking'
        - 'Public API'
      summary: 'Delete Compensation Benchmark Source'
      description: 'Deletes a benchmark source together with all benchmarks currently attached to it. The source `id` is taken from the request body. Use `GET /api/v1/compensation/benchmarks/sources` to look up the `id` of the source to delete. Returns `{ "result": "success" }` when the source is removed.'
      operationId: delete-compensation-benchmark-source
      requestBody:
        content:
          application/json:
            schema:
              title: DeleteCompensationBenchmarkSourceRequest
              required:
                - id
              properties:
                id:
                  description: 'ID of the benchmark source to delete. Must be non-empty.'
                  type: string
              type: object
      responses:
        '200':
          description: 'Object containing `{ "result": "success" }` when the source is deleted.'
          content:
            application/json:
              schema:
                title: DeleteCompensationBenchmarkSourceResponse
                properties:
                  result: { type: string }
                type: object
        '400':
          description: 'The id is missing, empty, or not a valid non-zero numeric identifier.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'The authenticated caller lacks permission to modify compensation benchmarks.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Unexpected server error while deleting the benchmark source.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - compensation_benchmarks.write
  /api/v1/compensation/benchmarks/import:
    post:
      tags:
        - 'Compensation Benchmarking'
        - 'Public API'
      summary: 'Import Compensation Benchmarks From CSV'
      description: 'Parses a CSV of compensation benchmarks uploaded as multipart/form-data and returns the parsed rows together with a suggested column-to-field mapping. The response is a preview payload — callers must follow up with the publish step in the UI/API to persist benchmarks. Returns `400` when no `file` part is provided and `422` when the CSV is malformed.'
      operationId: import-compensation-benchmarks
      requestBody:
        description: 'Multipart form containing a single `file` part with the CSV upload.'
        required: true
        content:
          multipart/form-data:
            schema:
              title: ImportCompensationBenchmarksRequest
              required:
                - file
              properties:
                file:
                  description: 'CSV file to import.'
                  type: string
                  format: binary
              type: object
      responses:
        '200':
          description: 'Parsed CSV preview containing the raw rows and a suggested column map.'
          content:
            application/json:
              schema:
                title: ImportCompensationBenchmarksResponse
                properties:
                  uploadData: { description: 'Parsed CSV rows. Each row is an array of cell values as strings.', type: array, items: { title: ImportCompensationBenchmarksRow, type: array, items: { type: string } } }
                  columnMap: { description: 'Suggested mapping between expected benchmark fields and CSV column headers.', type: array, items: { $ref: '#/components/schemas/CompensationBenchmarking-ColumnMap' } }
                type: object
        '400':
          description: 'The multipart request is missing the `file` part.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'The authenticated caller lacks permission to import compensation benchmarks.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'The uploaded CSV could not be parsed (missing columns, invalid data, etc.).'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Unexpected server error while parsing the CSV upload.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - compensation_benchmarks.write
  /api/v1/employees:
    get:
      tags:
        - Employees
        - 'Public API'
      summary: 'List Employees'
      description: "Returns a cursor-paginated collection of employees for the authenticated caller's company. The response is a JSON object with `data` (an array of employee records), `meta.total` (count of all employees matching the filter, not just the current page), `meta.page` (cursor pagination state), and `_links` (`self`, plus `next` / `prev` when more pages exist). Each employee record always includes the default identity and job fields, plus any additional fields requested via `fields`. `employeeId` is returned as a string. Field values the caller cannot read are returned as `null`, and the names of those suppressed fields are listed on the record in `_restrictedFields`; if the caller cannot read a field used in `filter` or `sort`, the affected employee is dropped from the result set entirely to avoid leaking presence. IDs for `filter[ids]` come from prior responses of this endpoint. Use this for lightweight directory-style listings and batch lookups by ID. For a single employee with the full set of fields, use Get Employee (`get-employee`) instead. For complex filtering (date ranges, multi-field predicates, OR/IN logic), arbitrary sorting, or tabular reports across many fields, use Get Data from Dataset (v2) (`get-data-from-dataset-v2`) instead."
      operationId: list-employees
      parameters:
        -
          name: filter
          in: query
          description: 'Filters used to match employees. Encode filter properties using deepObject style (`filter[firstName]=Ava`). Multiple filter fields are combined with AND. `filter[ids]` accepts either repeated keys (`filter[ids][]=123&filter[ids][]=124`) or a single comma-separated string (`filter[ids]=123,124`); both forms are supported.'
          required: false
          style: deepObject
          explode: true
          schema:
            $ref: '#/components/schemas/GetEmployeesFilterRequestObject'
        -
          name: sort
          in: query
          description: 'Comma-separated list of sortable fields. Prefix a field with `-` for descending order. Allowed fields: `employeeId`, `firstName`, `lastName`, `preferredName`, `jobTitleName`, `status`. Nulls sort first in ascending order and last in descending order. An invalid sort field returns a `BadRequest` error.'
          required: false
          schema:
            type: string
          example: '-lastName,firstName'
        -
          name: fields
          in: query
          description: "Additional fields to include in each employee record beyond the default set. The canonical form is a comma-separated list (`fields=workEmail,mobilePhone`); for backward compatibility the endpoint also accepts the bracket-array form (`fields[]=workEmail&fields[]=mobilePhone`). Note: plain repeated keys without brackets (`fields=workEmail&fields=mobilePhone`) are unreliable — most HTTP stacks keep only the last value, silently dropping earlier ones; use the comma-separated form instead. Unrecognized field names are silently ignored. Returned values are subject to permission checks — fields the caller cannot read are returned as `null` and their names are listed in the record's `_restrictedFields` array."
          required: false
          style: form
          explode: false
          schema:
            type: array
            items:
              $ref: '#/components/schemas/EmployeeOptionalField'
          example: 'workEmail,mobilePhone'
        -
          name: page
          in: query
          description: "Cursor-based pagination parameters. `page[limit]` controls page size (default 250, maximum 2500). `page[after]` and `page[before]` accept opaque cursors returned in the previous response's `meta.page.nextCursor` / `prevCursor`; do not specify both at once. The response's `_links.next` / `_links.prev` are pre-built URLs that already encode the correct cursor for the next or previous page."
          required: false
          style: deepObject
          explode: true
          schema:
            $ref: '#/components/schemas/EmployeeCursorPaginationQueryObject'
      responses:
        '200':
          description: 'Paginated list of employees.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetEmployeesResponseObject'
              examples:
                basic:
                  summary: 'Basic paginated result'
                  value: { data: [{ employeeId: '123', firstName: Ava, lastName: Nguyen, preferredName: Ava, photoUrl: 'https://cdn.example.com/p/123.jpg', jobTitleName: 'Engineering Manager', status: Active, _restrictedFields: [] }, { employeeId: '124', firstName: Sam, lastName: Park, preferredName: Sam, photoUrl: null, jobTitleName: 'Product Designer', status: Active, _restrictedFields: [photoUrl] }], meta: { total: 2456, page: { limit: 100, nextCursor: eyJuZXh0RW1wbG95ZWUiOjEyNX0=, prevCursor: null } }, _links: { self: { href: 'https://example.bamboohr.com/api/v1/employees?page%5Blimit%5D=100' }, next: { href: 'https://example.bamboohr.com/api/v1/employees?page%5Blimit%5D=100&page%5Bafter%5D=eyJuZXh0RW1wbG95ZWUiOjEyNX0%3D' } } }
                batchByIds:
                  summary: 'Batch fetch by a list of employee IDs'
                  value: { data: [{ employeeId: '200', firstName: Lena, lastName: Smith, preferredName: Lena, photoUrl: null, jobTitleName: 'QA Analyst', status: Active, _restrictedFields: [] }, { employeeId: '207', firstName: Marco, lastName: Lee, preferredName: null, photoUrl: null, jobTitleName: 'Support Engineer', status: Inactive, _restrictedFields: [photoUrl] }], meta: { total: 2, page: { limit: 250, nextCursor: null, prevCursor: null } }, _links: { self: { href: 'https://example.bamboohr.com/api/v1/employees?filter%5Bids%5D=200%2C207' } } }
        '400':
          description: 'Invalid request parameters. The `error.code` is `BadRequest` for filter, sort, or field validation failures (for example an unknown sort field, or `filter[status]` outside `active` / `inactive`) and `BadPage` when the `page` cursor is malformed, the wrong cursor direction is supplied, or the boundary employee changed between calls (for example, was deleted or no longer matches the filter).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Employee-StringCodeErrorResponse-V1'
        '401':
          description: Unauthorized.
        '429':
          description: 'Too many requests.'
        '500':
          description: 'Internal server error.'
      security:
        -
          basic: []
        -
          oauth:
            - employee
        -
          oauth:
            - 'employee:name'
        -
          oauth:
            - 'employee:job'
        -
          oauth:
            - employee_directory
        -
          oauth:
            - 'sensitive_employee:protected_info'
    post:
      tags:
        - Employees
        - 'Public API'
      summary: 'Create Employee'
      description: "Create a new employee. At minimum, provide a first name and last name in a JSON object or XML document. The request body schema lists commonly used fields, but any valid writable employee field name may be included as a key. To discover available field names, call the List Fields endpoint (operationId: list-fields, GET /api/v1/meta/fields).\n\nThis endpoint does not upload, set, or remove the employee profile photo. Photo-related keys (e.g. `photo`, `photoUrl`) included in the body are silently ignored: the request still creates the employee and returns 201, but no photo is attached. After creating the employee, use the Upload Employee Photo endpoint (operationId: upload-employee-photo, POST /api/v1/employees/{employeeId}/photo) to set the photo.\n\nTrax Payroll note: Employees added to a pay schedule synced with Trax Payroll must include the required payroll-related employee fields: employeeNumber (unless the company has automatic employee numbers enabled), firstName, lastName, dateOfBirth, ssn or ein, gender, maritalStatus, hireDate, address1, city, state, zipcode, country, employmentHistoryStatus, exempt, payType, payRate, payPer, overtimeRate, and location."
      operationId: create-employee
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/postNewEmployee'
          application/xml:
            schema:
              $ref: '#/components/schemas/postNewEmployee'
          text/xml:
            schema:
              $ref: '#/components/schemas/postNewEmployee'
      responses:
        '201':
          description: 'Employee created successfully.'
          headers:
            Location:
              description: 'The URL to view the employee in the web app. The ID of the employee will be included.'
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetEmployeeResponse'
        '400':
          description: 'If the posted XML or JSON is invalid or the minimum fields are not provided.'
        '403':
          description: 'If the API user does not have permission to add an employee.'
        '409':
          description: 'If an employee field was given an invalid value.'
      security:
        -
          basic: []
        -
          oauth:
            - employee.write
        -
          oauth:
            - 'employee:assets.write'
        -
          oauth:
            - 'employee:contact.write'
        -
          oauth:
            - 'employee:compensation.write'
        -
          oauth:
            - 'employee:custom_fields_encrypted.write'
        -
          oauth:
            - 'employee:custom_fields.write'
        -
          oauth:
            - 'employee:demographic.write'
        -
          oauth:
            - 'employee:dependent.write'
        -
          oauth:
            - 'employee:dependent:ssn.write'
        -
          oauth:
            - 'employee:education.write'
        -
          oauth:
            - 'employee:emergency_contacts.write'
        -
          oauth:
            - 'employee:identification.write'
        -
          oauth:
            - 'employee:job.write'
        -
          oauth:
            - 'employee:management.write'
        -
          oauth:
            - 'employee:name.write'
        -
          oauth:
            - 'employee:photo.write'
        -
          oauth:
            - 'employee:vaccination.write'
        -
          oauth:
            - 'sensitive_employee:protected_info.write'
        -
          oauth:
            - 'sensitive_employee:address.write'
        -
          oauth:
            - 'sensitive_employee:creditcards.write'
        -
          oauth:
            - 'employee:payroll.write'
        -
          oauth:
            - employee
        -
          oauth:
            - 'employee:job'
  '/api/v1/employees/{id}':
    get:
      tags:
        - Employees
        - 'Public API'
      summary: 'Get Employee'
      description: 'Returns a single employee record as a JSON object (or XML when `Accept: application/xml`). The `id` field is always present and is returned as a string; every other field is included only when explicitly named in the `fields` query parameter. With no `fields` parameter, the response contains only `id` — there is no implicit default field set. Field names come from List Fields (`list-fields`), which also exposes custom-field aliases usable here. By default only currently effective values from historical tables (job title, compensation, employment status, etc.) are returned; pass `onlyCurrent=false` to include future-dated values. Field-level permissions are applied silently: any requested field the authenticated caller cannot view is omitted from the response with no marker — an absent field may indicate either that it was not requested or that the caller lacks permission to view it. The maximum number of fields per request is 400. Use this for fetching arbitrary fields on a single known employee. For batch lookups or directory-style listings across employees, use List Employees (`list-employees`) instead. For complex filtering, multi-field predicates, or tabular reports, use Get Data from Dataset (v2) (`get-data-from-dataset-v2`) instead.'
      operationId: get-employee
      parameters:
        -
          name: id
          in: path
          description: 'The employee ID. The sentinel value `0` resolves to the employee record bound to the authenticated user, when one exists; if the credentials are not bound to an employee (for example an integration-style account), `0` returns only `{"id": "0"}` with no other fields. List Employees (`list-employees`) does not accept this sentinel.'
          required: true
          schema:
            type: string
        -
          name: fields
          in: query
          description: 'Comma-separated list of fields to include in the response. Three reference forms are accepted and may be mixed in a single request: standard field names (e.g. `firstName`, `workEmail`), numeric field IDs (e.g. `1349`), and custom-field aliases (e.g. `customStartDate`). Discover all three via List Fields (`list-fields`) — its response includes `id`, `name`, and `alias` for every available field. Example mixing all three: `firstName,1349,customStartDate`. When omitted, the response includes only `id`. Bracket-array (`fields[]=...`) and repeated-key (`fields=a&fields=b`) forms are not supported on this endpoint — use the comma-separated form. Unknown or unauthorized fields are silently dropped from the response.'
          required: false
          schema:
            type: string
        -
          name: onlyCurrent
          in: query
          description: 'When `true` (the default), returns only currently effective values from historical tables (job, compensation, employment status, etc.). When `false`, future-dated history rows are also returned.'
          required: false
          schema:
            type: boolean
            default: true
        -
          $ref: '#/components/parameters/AcceptHeaderParameter'
      responses:
        '200':
          description: 'An object keyed by field name with the requested employee''s values. `id` is always present and is a string. Fields the authenticated caller cannot view are silently omitted. When `Accept: application/xml`, the body is an `<employee id="...">` element with one `<field id="...">` child per returned field.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetEmployeeResponse'
            application/xml:
              schema:
                $ref: '#/components/schemas/GetEmployeeResponse'
        '400':
          description: 'The `fields` parameter contained more than 400 entries.'
        '401':
          description: Unauthorized.
        '403':
          description: 'The authenticated user does not have permission to view this employee.'
        '404':
          description: 'No employee was found for the supplied ID.'
      security:
        -
          basic: []
        -
          oauth:
            - employee
        -
          oauth:
            - 'employee:assets'
        -
          oauth:
            - 'employee:contact'
        -
          oauth:
            - 'employee:compensation'
        -
          oauth:
            - 'employee:custom_fields_encrypted'
        -
          oauth:
            - 'employee:custom_fields'
        -
          oauth:
            - 'employee:demographic'
        -
          oauth:
            - 'employee:dependent'
        -
          oauth:
            - 'employee:dependent:ssn'
        -
          oauth:
            - 'employee:education'
        -
          oauth:
            - 'employee:emergency_contacts'
        -
          oauth:
            - 'employee:identification'
        -
          oauth:
            - 'employee:job'
        -
          oauth:
            - 'employee:management'
        -
          oauth:
            - 'employee:name'
        -
          oauth:
            - 'employee:photo'
        -
          oauth:
            - 'employee:vaccination'
        -
          oauth:
            - 'sensitive_employee:protected_info'
        -
          oauth:
            - 'sensitive_employee:address'
        -
          oauth:
            - 'sensitive_employee:creditcards'
        -
          oauth:
            - 'employee:job.write'
    post:
      tags:
        - Employees
        - 'Public API'
      summary: 'Update Employee'
      description: "Update an employee's fields by submitting a JSON object or XML document containing field name/value pairs. The request body schema lists commonly used fields, but any valid writable employee field name may be used as a key. To discover available field names, call the List Fields endpoint (operationId: list-fields, GET /api/v1/meta/fields).\n\nThis endpoint does not upload, replace, or remove the employee profile photo, and does not accept any binary or file uploads in general. Photo-related keys (e.g. `photo`, `photoUrl`) included in the body are silently ignored: the request still returns 200, but no photo change is made. To change a photo, use the Upload Employee Photo endpoint (operationId: upload-employee-photo, POST /api/v1/employees/{employeeId}/photo).\n\nTrax Payroll note: If the employee is currently on a pay schedule syncing with Trax Payroll, or is being added to one, the request must include the required payroll-related employee fields: employeeNumber (unless the company has automatic employee numbers enabled), firstName, lastName, dateOfBirth, ssn or ein, gender, maritalStatus, hireDate, address1, city, state, zipcode, country, employmentHistoryStatus, exempt, payType, payRate, payPer, overtimeRate, and location."
      operationId: update-employee
      parameters:
        -
          name: id
          in: path
          description: 'The employee ID.'
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Employee'
          application/xml:
            schema:
              $ref: '#/components/schemas/Employee'
          text/xml:
            schema:
              $ref: '#/components/schemas/Employee'
      responses:
        '200':
          description: 'Employee updated successfully.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GetEmployeeResponse'
        '400':
          description: 'Provided JSON is malformed, or required fields are missing.'
        '403':
          description: 'The API user does not have permission to see the employee or to update any of the requested fields.'
        '404':
          description: 'The employee does not exist.'
        '409':
          description: 'A field was given an invalid value (e.g., duplicate email, invalid state/country, incompatible pay type).'
      security:
        -
          basic: []
        -
          oauth:
            - employee.write
        -
          oauth:
            - 'employee:assets.write'
        -
          oauth:
            - 'employee:contact.write'
        -
          oauth:
            - 'employee:compensation.write'
        -
          oauth:
            - 'employee:custom_fields_encrypted.write'
        -
          oauth:
            - 'employee:custom_fields.write'
        -
          oauth:
            - 'employee:demographic.write'
        -
          oauth:
            - 'employee:dependent.write'
        -
          oauth:
            - 'employee:dependent:ssn.write'
        -
          oauth:
            - 'employee:education.write'
        -
          oauth:
            - 'employee:emergency_contacts.write'
        -
          oauth:
            - 'employee:identification.write'
        -
          oauth:
            - 'employee:job.write'
        -
          oauth:
            - 'employee:management.write'
        -
          oauth:
            - 'employee:name.write'
        -
          oauth:
            - 'employee:photo.write'
        -
          oauth:
            - 'employee:vaccination.write'
        -
          oauth:
            - 'sensitive_employee:protected_info.write'
        -
          oauth:
            - 'sensitive_employee:address.write'
        -
          oauth:
            - 'sensitive_employee:creditcards.write'
        -
          oauth:
            - 'employee:payroll.write'
        -
          oauth:
            - employee
        -
          oauth:
            - 'employee:job'
    delete:
      tags:
        - Employees
        - 'Public API'
      summary: 'Delete employee'
      description: 'Permanently deletes an employee record and all associated data.'
      operationId: delete-employee
      parameters:
        -
          name: id
          in: path
          description: 'ID of the employee to delete.'
          required: true
          schema:
            type: integer
      responses:
        '204':
          description: 'Employee deleted successfully.'
        '403':
          description: 'Insufficient permissions.'
        '500':
          description: 'Internal server error.'
        '404':
          description: 'Employee not found.'
      security:
        -
          oauth:
            - employee.write
  '/api/v1/employees/{id}/tables/{table}/{rowId}':
    post:
      tags:
        - 'Employee Tables'
        - 'Public API'
      summary: 'Update Table Row'
      description: 'Update an existing row in the specified employee table by submitting field name/value pairs for the fields to change in JSON or XML.'
      operationId: update-table-row
      parameters:
        -
          name: id
          in: path
          description: 'The employee ID.'
          required: true
          schema:
            type: string
        -
          name: table
          in: path
          description: 'The API name of the table containing the row to update. See the TableName schema for valid standard values; custom tables use a `custom` prefix. Use GET /api/v1/meta/tables to discover the complete list.'
          required: true
          schema:
            $ref: '#/components/schemas/TableName'
        -
          name: rowId
          in: path
          description: 'The ID of the row to update.'
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TableRowUpdate'
          application/xml:
            schema:
              $ref: '#/components/schemas/TableRowUpdate'
          text/xml:
            schema:
              $ref: '#/components/schemas/TableRowUpdate'
      responses:
        '200':
          description: 'Row updated successfully. A 200 response is returned as long as at least one field was updated, even if some fields were skipped due to permissions. No response body is returned.'
        '400':
          description: 'The posted JSON or XML is malformed, or required fields are missing.'
        '403':
          description: 'Permission denied.'
        '404':
          description: 'The employee, table, or row does not exist.'
        '406':
          description: 'One or more field values are invalid.'
        '409':
          description: 'Conflict. The provided field values conflict with business rules or payroll constraints.'
        '412':
          description: 'Precondition failed. The update requires an active pay schedule or other required prerequisite data.'
      security:
        -
          basic: []
        -
          oauth:
            - employee.write
        -
          oauth:
            - 'employee:job.write'
        -
          oauth:
            - 'employee:compensation.write'
        -
          oauth:
            - 'employee:custom_fields.write'
        -
          oauth:
            - 'employee:custom_fields_encrypted.write'
        -
          oauth:
            - 'employee:assets.write'
        -
          oauth:
            - 'employee:emergency_contacts.write'
        -
          oauth:
            - 'sensitive_employee:creditcards.write'
        -
          oauth:
            - 'employee:education.write'
    delete:
      tags:
        - 'Employee Tables'
        - 'Public API'
      summary: 'Delete Employee Table Row'
      description: "Deletes a specific row from an employee's tabular data. The table name identifies which tabular dataset to target (e.g., jobInfo, compensation, customTabularField). Returns `success: true` if the row was deleted, or `success: false` with an error message if the row was not found or could not be deleted. Deletion will fail with a 409 if the row has pending approval changes, or a 412 if the row is tied to an active pay schedule. Per-table field schemas are available as named OpenAPI components (e.g., `JobInfoTableRowRequest`, `CompensationTableRowRequest`). See the components/schemas section of this spec for the full list."
      operationId: delete-employee-table-row
      parameters:
        -
          name: id
          in: path
          description: 'The employee ID.'
          required: true
          schema:
            type: string
        -
          name: table
          in: path
          description: 'The API name of the table containing the row to delete. See the TableName schema for valid standard values; custom tables use a `custom` prefix (e.g., `customTabularField`). Use the `list-tabular-fields` tool (GET /api/v1/meta/tables) to discover the complete list.'
          required: true
          schema:
            $ref: '#/components/schemas/TableName'
        -
          name: rowId
          in: path
          description: 'The ID of the specific row to delete.'
          required: true
          schema:
            type: string
      responses:
        '200':
          description: 'Returns `success: true` if the row was deleted. Returns `success: false` with an error message if the row was not found or could not be deleted.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TableRowDeleteResponse'
        '400':
          description: 'Bad request. Invalid employee ID or table name.'
        '401':
          description: Unauthorized.
        '403':
          description: 'Permission denied. The caller lacks write access to this table for the specified employee.'
        '409':
          description: 'Conflict. The row has pending approval changes and cannot be deleted until they are resolved.'
        '412':
          description: 'Precondition failed. The row cannot be deleted because it is tied to an active pay schedule.'
      security:
        -
          basic: []
        -
          oauth:
            - employee.write
        -
          oauth:
            - 'employee:job.write'
        -
          oauth:
            - 'employee:compensation.write'
        -
          oauth:
            - 'employee:custom_fields.write'
        -
          oauth:
            - 'employee:custom_fields_encrypted.write'
        -
          oauth:
            - 'employee:assets.write'
        -
          oauth:
            - 'employee:emergency_contacts.write'
        -
          oauth:
            - 'sensitive_employee:creditcards.write'
        -
          oauth:
            - 'employee:education.write'
  '/api/v1/files/{fileId}':
    get:
      tags:
        - 'Company Files'
        - 'Public API'
      summary: 'Get Company File'
      description: "Downloads a company file by its ID. The response body is the raw file content. The `Content-Type` header reflects the file's MIME type and `Content-Disposition` is set to `attachment` with the original filename. Access is permitted if the file or its category is shared with employees, shared directly with the requesting user, or the user has view permission on the file section."
      operationId: get-company-file
      parameters:
        -
          name: fileId
          in: path
          description: 'The ID of the company file to download.'
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: "The raw file content. The Content-Type header reflects the file's MIME type."
          content:
            application/octet-stream: {  }
        '403':
          description: 'The API user does not have permission to access this file.'
        '404':
          description: 'No file exists with the given ID.'
      security:
        -
          basic: []
        -
          oauth:
            - company_file
    post:
      tags:
        - 'Company Files'
        - 'Public API'
      summary: 'Update Company File'
      description: 'Updates metadata for an existing company file. Supports renaming the file, moving it to a different category, and toggling employee visibility. Accepts JSON or XML. Only fields included in the request body are updated.'
      operationId: update-company-file
      parameters:
        -
          name: fileId
          in: path
          description: 'The ID of the company file to update.'
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CompanyFileUpdate'
      responses:
        '200':
          description: 'The company file was updated successfully.'
        '400':
          description: 'The request body contains malformed JSON or XML.'
        '403':
          description: 'The API user does not have permission to modify this file or its category.'
        '404':
          description: 'The file or the target category was not found.'
      security:
        -
          basic: []
        -
          oauth:
            - company_file.write
    delete:
      tags:
        - 'Company Files'
        - 'Public API'
      summary: 'Delete Company File'
      description: 'Permanently removes a company file and its associated storage. The company must have the Files tool enabled; otherwise the file is treated as not found. Read-only file types (e.g. e-signature templates) are silently skipped. No response body is returned on success. Use "Company Files > List Company Files" to obtain file IDs.'
      operationId: delete-company-file
      parameters:
        -
          name: fileId
          in: path
          description: 'The ID of the company file to delete.'
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'The company file was deleted successfully. No response body is returned.'
        '403':
          description: 'The API user does not have permission to delete the requested file.'
        '404':
          description: 'The requested file was not found, or the Files tool is not enabled for the company.'
        '500':
          description: 'An internal server error occurred.'
      security:
        -
          basic: []
        -
          oauth:
            - company_file.write
  '/api/v1/employees/{id}/files/{fileId}':
    get:
      tags:
        - 'Employee Files'
        - 'Public API'
      summary: 'Get Employee File'
      description: "Downloads the binary content of an employee file as an attachment. The response Content-Type header reflects the file's stored MIME type (e.g. application/pdf) and includes a Content-Disposition header with the original filename. The special employee ID of 0 resolves to the employee associated with the API key.\n\nUse \"List Employee Files\" to discover file IDs and their categories for a given employee. Archived or soft-deleted files are excluded and return 404."
      operationId: get-employee-file
      parameters:
        -
          name: id
          in: path
          description: 'The ID of the employee whose file is being retrieved. Use 0 to resolve to the employee associated with the API key.'
          required: true
          schema:
            type: integer
        -
          name: fileId
          in: path
          description: 'The ID of the employee file to download.'
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: "The employee file content streamed as a binary download. The Content-Type matches the file's stored MIME type and a Content-Disposition header provides the original filename."
          content:
            application/octet-stream:
              schema:
                title: 'Employee File Download'
                type: string
                format: binary
        '403':
          description: "The API user does not have permission to access the requested employee or the file's section."
        '404':
          description: 'The requested employee file was not found, or it has been archived or soft-deleted.'
      security:
        -
          basic: []
        -
          oauth:
            - 'employee:file'
    post:
      tags:
        - 'Employee Files'
        - 'Public API'
      summary: 'Update Employee File'
      description: "Updates metadata for an existing employee file. Supports renaming the file, moving it to a different category, and toggling employee visibility. Accepts JSON or XML; only fields present in the request body are updated. An empty XML document no-ops successfully, while an empty JSON body returns 400. The `categoryId` field is silently ignored when the caller authenticated as the file creator but lacks full file permissions. Moving a file to a new category requires view/edit access to both the current and target category. Returns 403 if the file belongs to a read-only file section. Use 'List Employee Files' to obtain file IDs and category IDs."
      operationId: update-employee-file
      parameters:
        -
          name: id
          in: path
          description: 'The ID of the employee whose file is being updated.'
          required: true
          schema:
            type: integer
        -
          name: fileId
          in: path
          description: 'The ID of the employee file to update.'
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EmployeeFileUpdate'
          application/xml:
            schema:
              $ref: '#/components/schemas/EmployeeFileUpdate'
      responses:
        '200':
          description: 'The employee file metadata was updated successfully. No response body is returned.'
        '400':
          description: 'The request body contains malformed JSON or XML.'
        '403':
          description: "The API user does not have permission to modify the requested employee's file, its category, or the file belongs to a read-only section."
        '404':
          description: 'The requested employee file or target category was not found.'
        '500':
          description: 'An internal server error occurred.'
      security:
        -
          basic: []
        -
          oauth:
            - 'employee:file.write'
    delete:
      tags:
        - 'Employee Files'
        - 'Public API'
      summary: 'Delete Employee File'
      description: "Permanently deletes an employee file. This action cannot be undone and removes the file from storage. The special employee ID of 0 resolves to the employee associated with the API key. Returns 200 even if the file was already deleted (idempotent). Returns 403 if the caller lacks permission or the file belongs to a BambooPayroll-managed section. Returns 404 if the file does not exist for the specified employee or the Files tool is not enabled for the company. Use 'List Employee Files' to obtain file IDs."
      operationId: delete-employee-file
      parameters:
        -
          name: id
          in: path
          description: 'The ID of the employee whose file is being deleted. Use 0 to default to the employee associated with the API key.'
          required: true
          schema:
            type: integer
        -
          name: fileId
          in: path
          description: 'The ID of the employee file to delete.'
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'The employee file was deleted successfully, or had already been deleted for this employee.'
        '403':
          description: "The caller does not have permission to delete this employee's file, or the file belongs to a BambooPayroll-managed section and the caller is not a payroll admin."
        '404':
          description: 'The file was not found for the specified employee, or the Files tool is not enabled for this company.'
        '500':
          description: 'An internal server error occurred.'
      security:
        -
          basic: []
        -
          oauth:
            - 'employee:file.write'
  /api/v1/pay-grades-and-bands/job-titles-with-employees:
    get:
      tags:
        - 'Public API'
      summary: 'List job titles with employees'
      description: 'Get job titles with their associated employees for levels and bands configuration.'
      operationId: 3af7d1ac36c35008b8ebb93fecbdb35b
      responses:
        '200':
          description: 'Successful response'
          content:
            application/json:
              schema:
                title: PayGradesAndBandsJobTitlesWithEmployeesResponse
                type: array
                items:
                  $ref: '#/components/schemas/LevelsAndBands-JobTitleWithEmployees'
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - pay_grades_and_bands
  /api/v1/pay-grades-and-bands/status-counts:
    get:
      tags:
        - 'Public API'
      summary: 'Get compensation level group status counts'
      description: 'Get the count of compensation level groups in each status (draft, published, historic).'
      operationId: 99b887959832c80eead3a176acc0c2d4
      responses:
        '200':
          description: 'Successful response'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LevelsAndBands-GroupStatusCounts'
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - pay_grades_and_bands
  /api/v1/pay-grades-and-bands/status:
    get:
      tags:
        - 'Public API'
      summary: 'Get levels and bands status'
      description: 'Get the validation status of each step in the levels and bands configuration wizard.'
      operationId: 6fbb2fe60e374d2204f1222252105945
      responses:
        '200':
          description: 'Successful response'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LevelsAndBands-LevelsAndBandsStatus'
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - pay_grades_and_bands
  /api/v1/pay-grades-and-bands/levels:
    get:
      tags:
        - 'Public API'
      summary: 'List compensation level groups and levels'
      description: 'Get all compensation level groups and levels with validation errors and warnings.'
      operationId: db8c61787c931b69afbf9e501be85256
      responses:
        '200':
          description: 'Successfully retrieved groups and levels'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LevelsAndBands-LevelsAndBands'
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - pay_grades_and_bands
    put:
      tags:
        - 'Public API'
      summary: 'Save compensation level groups and levels'
      description: 'Upsert compensation level groups and levels. Creates new groups or levels or updates existing ones. Does not delete existing data not included in the request.'
      operationId: 432e469fe5959a6ccd765d02018e7db4
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LevelsAndBands-LevelsAndBands'
      responses:
        '200':
          description: 'Successfully saved groups and levels'
          content:
            application/json:
              schema:
                title: PayGradesAndBandsSaveLevelsResponse
                properties:
                  status: { type: string, example: success }
                type: object
        '400':
          description: 'Invalid request body'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - pay_grades_and_bands.write
  /api/v1/pay-grades-and-bands/pay-bands:
    get:
      tags:
        - 'Public API'
      summary: 'Get pay bands for levels'
      description: 'Get pay bands for each compensation level with validation errors and warnings. If percentageRange is null, band type is min-mid-max; otherwise it is percentage-based.'
      operationId: fb29d4d4790104b6892a4b6d6db67e19
      responses:
        '200':
          description: 'Successful response'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LevelsAndBands-LevelsAndBands'
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - pay_grades_and_bands
    put:
      tags:
        - 'Public API'
      summary: 'Update pay bands'
      description: 'Bulk update pay bands for compensation levels.'
      operationId: ad7871529b2a9c6612f8dd3c62192c08
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - payBands
                - payBandType
              properties:
                payBandType:
                  type: string
                  enum: [percentRange, minMidMax]
                payBands:
                  type: array
                  items: { $ref: '#/components/schemas/LevelsAndBands-PayBand' }
              type: object
      responses:
        '200':
          description: 'Successfully updated pay bands'
          content:
            application/json:
              schema:
                title: PayGradesAndBandsUpdatePayBandsResponse
                properties:
                  status: { type: string, example: success }
                type: object
        '400':
          description: 'Invalid pay bands payload'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - pay_grades_and_bands.write
  /api/v1/pay-grades-and-bands/job-titles:
    get:
      tags:
        - 'Public API'
      summary: 'Get job titles and level assignments'
      description: 'Get job titles with their associated compensation levels and all groups/levels. Returns job titles mapped to level IDs and complete group/level hierarchy.'
      operationId: 4acdbb25aa278f9f94b44b42ef9f1b27
      responses:
        '200':
          description: 'Successful response'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LevelsAndBands-LevelsAndBands'
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - pay_grades_and_bands
    put:
      tags:
        - 'Public API'
      summary: 'Update job title level assignments'
      description: 'Replace draft job title-to-level assignments with the provided associations.'
      operationId: 2a0b847ceec3a4f9bfe20cf27e10e81a
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/LevelsAndBands-JobTitleAssignmentsRequest'
      responses:
        '200':
          description: 'Successfully updated job title assignments'
          content:
            application/json:
              schema:
                title: PayGradesAndBandsUpdateJobTitlesResponse
                properties:
                  status: { type: string, example: success }
                type: object
        '400':
          description: 'Invalid request body'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - pay_grades_and_bands.write
  /api/v1/pay-grades-and-bands/review:
    get:
      tags:
        - 'Public API'
      summary: 'Get levels and bands review data'
      description: 'Get consolidated review data for levels and bands with all validation errors and warnings across all steps.'
      operationId: 12c40475623893cf0434b3ae69e36975
      responses:
        '200':
          description: 'Successful response'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LevelsAndBands-LevelsAndBands'
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - pay_grades_and_bands
  /api/v1/pay-grades-and-bands:
    get:
      tags:
        - 'Public API'
      summary: 'Get published levels and bands'
      description: 'Get published levels and bands data including compensation level groups, levels, pay bands, and job title assignments.'
      operationId: cfacff2b34cf1e65ccd1ab188c5e1d12
      responses:
        '200':
          description: 'Successful response'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LevelsAndBands-LevelsAndBands'
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - pay_grades_and_bands
  /api/v1/pay-grades-and-bands/publish:
    post:
      tags:
        - 'Public API'
      summary: 'Publish draft compensation level groups'
      description: 'Publish all draft compensation level groups and mark any published groups as historic.'
      operationId: 22932a81ba129f22114ccdef397af933
      responses:
        '200':
          description: 'Successfully published draft groups'
          content:
            application/json:
              schema:
                title: PayGradesAndBandsPublishResponse
                properties:
                  status: { type: string, example: success }
                type: object
        '400':
          description: 'Invalid data'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - pay_grades_and_bands.write
  /api/v1/pay-grades-and-bands/import:
    post:
      tags:
        - 'Public API'
      summary: 'Import levels and bands from CSV'
      description: 'Parse and validate a levels and bands CSV upload and return suggested mappings and validation results.'
      operationId: cdd8659ca89fb38042fd56feab507edf
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              required:
                - file
              properties:
                file:
                  description: 'Levels and bands import CSV'
                  type: string
                  format: binary
              type: object
      responses:
        '200':
          description: 'Successfully parsed CSV'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LevelsAndBands-UploadResponse'
        '400':
          description: 'Multipart request missing file'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Uploaded CSV could not be parsed or validated'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - pay_grades_and_bands.write
  '/api/v1/pay-grades-and-bands/levels/{segment}':
    delete:
      tags:
        - 'Public API'
      summary: 'Delete groups by status or delete one level'
      description: 'Delete compensation level groups when `segment` is `draft`, `published`, or `historic`, or delete a single compensation level when `segment` is a numeric level identifier.'
      operationId: 0da9c9f851462d2e44dfe866b8e67e74
      parameters:
        -
          name: segment
          in: path
          description: 'Either `draft`, `published`, or `historic` to delete groups by status, or a numeric level id to delete one level.'
          required: true
          schema:
            type: string
            example: draft
      responses:
        '200':
          description: 'Successfully deleted groups (`{"status":"success"}`) or returned updated hierarchy after deleting a level.'
          content:
            application/json:
              schema:
                title: PayGradesAndBandsDeleteResponse
                type: object
        '400':
          description: 'Invalid segment'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Insufficient permissions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Level not found'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - pay_grades_and_bands.write
  '/api/v1/performance/employees/{employeeId}/goals/filters':
    get:
      tags:
        - Goals
        - 'Public API'
      summary: 'Get Goal Filters (v1)'
      description: 'Deprecated. Use "Get Goal Filters (v1.1)" instead. Get the number of goals per status for an employee. Returns a count of goals in each status (In Progress, Completed) for the specified employee. Goals with milestones are excluded from counts in this version.'
      operationId: get-goals-filters-v1
      parameters:
        -
          name: employeeId
          in: path
          description: 'employeeId is the employee ID to whom the goals are assigned.'
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Returns goal status filter counts. Goals with milestones are excluded in this version.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GoalFiltersV1'
        '403':
          description: 'The API user does not have permission to view goals for this employee.'
        '500':
          description: 'Something went wrong fetching filters.'
      deprecated: true
      security:
        -
          basic: []
        -
          oauth:
            - goal
  '/api/v1/performance/employees/{employeeId}/goals':
    get:
      tags:
        - Goals
        - 'Public API'
      summary: 'List Goals'
      description: 'Returns goals for an employee. Use this endpoint to locate goals by title before updating, commenting on, closing, reopening, or deleting them. Use `filter=status-inProgress` when the user asks for current/active goals, `filter=status-completed` for completed goals, `filter=status-closed` for closed goals, and `filter=status-all` when the user says "all goals", "including closed", or you are resolving a goal by title for a write operation where the status is unknown. If no filter is provided, closed goals are excluded. Results are capped at 50 goals; if a title lookup is ambiguous or absent, do not update or delete a different goal.'
      operationId: list-goals
      parameters:
        -
          name: employeeId
          in: path
          description: 'employeeId is the employee ID to whom the goals are assigned.'
          required: true
          schema:
            type: string
        -
          name: filter
          in: query
          description: 'Goal status filter. Use `status-inProgress` for current active goals, `status-completed` for completed goals, `status-closed` for closed goals, and `status-all` when locating a goal by title for update/delete/reopen or when the user asks for all goals. If omitted, closed goals are excluded. Unrecognized values behave like omission.'
          schema:
            type: string
            enum:
              - status-inProgress
              - status-completed
              - status-closed
              - status-all
      responses:
        '200':
          description: 'The response content will be a JSON document with the requested information.'
          content:
            application/json:
              schema:
                title: GoalsList
                properties:
                  goals: { description: 'All goals of the selected employee', type: array, items: { $ref: '#/components/schemas/TransformedApiGoal' } }
                type: object
        '403':
          description: 'The API user does not have permission to view goals for this employee.'
      security:
        -
          basic: []
        -
          oauth:
            - goal
    post:
      tags:
        - Goals
        - 'Public API'
      summary: 'Create Goal'
      description: 'Create a new goal for an employee. To create a simple goal without milestones, omit the `milestones` field; the goal''s progress can then be changed with `update-goal-progress`. To create a milestone-based goal, provide `milestones` as a non-empty array of `{ "title": string }` objects; the goal''s percent complete is then derived from milestone completion and should be changed via `update-goal-milestone-progress`. Sending `milestones: null` is treated as omitted (creates a simple goal).'
      operationId: create-goal
      parameters:
        -
          name: employeeId
          in: path
          description: 'employeeId is the employee ID with whom the goal is associated.'
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - title
                - dueDate
                - sharedWithEmployeeIds
              properties:
                title:
                  description: 'The title of the goal'
                  type: string
                  example: 'New Goal To update'
                description:
                  description: 'A detailed description of the goal'
                  type: string
                  example: 'This should be fun!'
                dueDate:
                  description: 'The due date for the goal in YYYY-MM-DD format'
                  type: string
                  format: date
                  example: '2025-12-17'
                percentComplete:
                  description: 'Initial percentage of completion for a simple goal (0-100). Defaults to 0 if omitted. Ignored when `milestones` is provided; milestone-based goals derive percent complete from milestone completion and should be updated via `update-goal-milestone-progress`.'
                  type: integer
                  maximum: 100
                  minimum: 0
                  example: 0
                completionDate:
                  description: 'The date when the goal was completed in YYYY-MM-DD format. Only valid when percentComplete is 100; providing this field with any other percentComplete value will result in an error.'
                  example: null
                  oneOf: [{ type: string, format: date }, { type: 'null' }]
                sharedWithEmployeeIds:
                  description: 'List of employee IDs with whom the goal is shared. Must include the employee ID of the goal owner.'
                  type: array
                  items: { type: integer }
                  example: [1234, 5678]
                alignsWithOptionId:
                  description: 'ID of the option this goal aligns with'
                  oneOf: [{ type: integer }, { type: 'null' }]
                milestones:
                  description: 'Optional. Provide a non-empty array of milestone objects to create a milestone-based goal. Omit this field (or send `null`) to create a simple goal.'
                  type: array
                  items: { properties: { title: { description: 'The title of the milestone', type: string, example: 'Surprise, it is a milestone' } }, type: object }
                  example: [{ title: 'Surprise, it is a milestone' }, { title: 'Now there are two of them!' }]
              type: object
      responses:
        '201':
          description: 'A goal object that includes the new goal.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransformedApiEmployeeGoalDetails'
        '400':
          description: 'The request body is invalid or required fields are missing or malformed (e.g. invalid dueDate, percentComplete out of range, completionDate provided with a non-100 percentComplete, sharedWithEmployeeIds not an array or missing the goal owner).'
          content:
            application/json: {  }
        '403':
          description: 'If the API user does not have permission to create a goal for this employee.'
          content:
            application/json: {  }
        '500':
          description: 'If there was a problem creating the goal.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - goal.write
  '/api/v1/performance/employees/{employeeId}/goals/{goalId}':
    put:
      tags:
        - Goals
        - 'Public API'
      summary: 'Update Goal (v1)'
      description: 'Deprecated. Use "Update Goal (v1.1)" instead. Update a goal. This version will not update a goal to contain milestones, that functionality is added in version 1.1.'
      operationId: update-goal-v1
      parameters:
        -
          name: employeeId
          in: path
          description: 'employeeId is the employee ID with whom the goal is associated.'
          required: true
          schema:
            type: string
        -
          name: goalId
          in: path
          description: 'goalId is the goal ID for the specified employee.'
          required: true
          schema:
            type: string
      requestBody:
        description: "Required fields: title, sharedWithEmployeeIds, dueDate. Omitted optional fields overwrite existing values using the endpoint's default behavior; see individual field descriptions for details."
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UpdateGoalV1'
      responses:
        '200':
          description: 'A successful response indicates that all the requested changes were made. The content of the response will be the goal response object for the specified goalId.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransformedApiEmployeeGoalDetails'
        '400':
          description: 'The posted JSON is invalid.'
          content:
            application/json: {  }
        '403':
          description: 'Goal is not editable or insufficient permissions.'
          content:
            application/json: {  }
        '404':
          description: 'The goal specified by the given goalId was not found.'
          content:
            application/json: {  }
        '500':
          description: 'If there was a problem updating the goal.'
          content:
            application/json: {  }
      deprecated: true
      security:
        -
          basic: []
        -
          oauth:
            - goal.write
    delete:
      tags:
        - Goals
        - 'Public API'
      summary: 'Delete Goal'
      description: 'Permanently deletes a goal for an employee. The goal must belong to the specified employee. Returns 204 with no response body on success. For natural-language requests that identify a goal by title or partial title, first call `list-goals` with `filter=status-all` and resolve exactly one matching goal. Do not delete if there are multiple matches or no match; ask for clarification or report that the goal was not found.'
      operationId: delete-goal
      parameters:
        -
          name: employeeId
          in: path
          description: 'employeeId is the employee ID with whom the goal is associated.'
          required: true
          schema:
            type: string
        -
          name: goalId
          in: path
          description: 'The exact goal ID for the specified employee. Do not infer this from a partial title unless you have resolved a single matching goal via `list-goals`.'
          required: true
          schema:
            type: string
      responses:
        '204':
          description: 'The goal was successfully deleted.'
        '403':
          description: 'The API user does not have permission to delete this goal.'
        '404':
          description: 'The goalId is zero or non-positive after integer cast, or the goal was not found.'
      security:
        -
          basic: []
        -
          oauth:
            - goal.write
  '/api/v1/performance/employees/{employeeId}/goals/{goalId}/progress':
    put:
      tags:
        - Goals
        - 'Public API'
      summary: 'Update Goal Progress'
      description: 'Update the progress percentage of a simple, non-milestone goal. Do not use this endpoint for goals that contain milestones; BambooHR derives percent complete for milestone-based goals from milestone completion and will reject manual percent updates with a 400 response (`UPDATE_GOAL_WITH_MILESTONE_PROGRESS_ERROR`). For milestone-based goals, use `update-goal-milestone-progress` to mark individual milestones complete or incomplete.'
      operationId: update-goal-progress
      parameters:
        -
          name: employeeId
          in: path
          description: 'employeeId is the employee ID with whom the goal is associated.'
          required: true
          schema:
            type: integer
        -
          name: goalId
          in: path
          description: 'goalId is the goal ID for the specified employee.'
          required: true
          schema:
            type: integer
      requestBody:
        description: 'The updated progress for a simple goal. Provide `percentComplete` from 0–100. When `percentComplete` is 100, provide `completionDate`; when less than 100, omit `completionDate` or set it to null. This endpoint is rejected for goals with milestones — use `update-goal-milestone-progress` instead.'
        required: true
        content:
          application/json:
            schema:
              required:
                - percentComplete
              properties:
                percentComplete:
                  description: 'The percentage of completion for the goal (0-100)'
                  type: integer
                  maximum: 100
                  minimum: 0
                  example: 100
                completionDate:
                  description: 'The date when the goal was completed in YYYY-MM-DD format. Required when percentComplete is 100.'
                  example: '2025-04-11'
                  oneOf: [{ type: string, format: date }, { type: 'null' }]
              type: object
      responses:
        '200':
          description: 'A successful response indicates that all the requested changes were made. The content of the response will be the goal response object for the specified goalId.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransformedApiEmployeeGoalDetails'
        '400':
          description: 'The request is invalid. Possible causes: percentComplete is out of range (0–100), the goal has milestones (use the milestone progress endpoint instead), or the specified employee is not associated with this goal.'
          content:
            application/json: {  }
        '403':
          description: 'Goal is not editable or insufficient permissions.'
          content:
            application/json: {  }
        '404':
          description: 'No goal found for the given goalId.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - goal.write
  '/api/v1/performance/employees/{employeeId}/goals/{goalId}/milestones/{milestoneId}/progress':
    put:
      tags:
        - Goals
        - 'Public API'
      summary: 'Update Milestone Progress'
      description: "Update the progress of a milestone in a goal. Validate the result using returned domain fields such as the milestone's `completedDateTime`, plus the goal's `status` and `percentComplete`; do not rely solely on `lastChangedDateTime` as the confirmation timestamp, because it may not match the HTTP response time or may reflect BambooHR's internal timezone/update semantics."
      operationId: update-goal-milestone-progress
      parameters:
        -
          name: employeeId
          in: path
          description: 'employeeId is the employee ID to whom the goals are assigned.'
          required: true
          schema:
            type: string
        -
          name: goalId
          in: path
          description: 'goalId is the goal ID for the specified employee.'
          required: true
          schema:
            type: string
        -
          name: milestoneId
          in: path
          description: 'milestoneId is the milestone ID for the specified goal.'
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - complete
              properties:
                complete:
                  description: 'Whether the milestone is complete or not'
                  type: boolean
                  example: true
              type: object
      responses:
        '200':
          description: 'The milestone progress was successfully updated. The response contains the updated goal details.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransformedApiEmployeeGoalDetails'
        '400':
          description: 'The request was invalid. Please check the request parameters and try again.'
        '403':
          description: 'User does not have permission to update this goal'
        '404':
          description: 'Goal or milestone not found'
        '500':
          description: 'Something went wrong updating your progress'
      security:
        -
          basic: []
        -
          oauth:
            - goal.write
  '/api/v1/performance/employees/{employeeId}/goals/{goalId}/sharedWith':
    put:
      tags:
        - Goals
        - 'Public API'
      summary: 'Update Goal Sharing'
      description: "Replaces the full list of employees this goal is shared with. The provided `sharedWithEmployeeIds` array must include the goal owner's employee ID. Returns the updated goal object."
      operationId: update-goal-sharing
      parameters:
        -
          name: employeeId
          in: path
          description: 'employeeId is the employee ID with whom the goal is associated.'
          required: true
          schema:
            type: string
        -
          name: goalId
          in: path
          description: 'goalId is the goal ID for the specified employee.'
          required: true
          schema:
            type: string
      requestBody:
        description: 'Employee IDs of employees with whom the goal is shared. All goal owners are considered "shared with".'
        required: true
        content:
          application/json:
            schema:
              properties:
                sharedWithEmployeeIds:
                  description: 'List of employee IDs with whom the goal is shared. Must include the employee ID of the goal owner.'
                  type: array
                  items: { type: integer }
              type: object
      responses:
        '200':
          description: 'A successful response indicates that all the requested changes were made. The content of the response will be the goal response object for the specified goalId.'
          content:
            application/json:
              schema:
                title: GoalResponse
                properties:
                  goal: { $ref: '#/components/schemas/TransformedApiGoal' }
                type: object
        '400':
          description: 'The request is invalid. Possible causes: sharedWithEmployeeIds is not an array, sharedWithEmployeeIds is empty, or the specified employee is not associated with this goal.'
          content:
            application/json: {  }
        '403':
          description: 'Goal is not editable or insufficient permissions.'
          content:
            application/json: {  }
        '404':
          description: 'No goal found for the given goalId.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - goal.write
  '/api/v1/performance/employees/{employeeId}/goals/aggregate':
    get:
      tags:
        - Goals
        - 'Public API'
      summary: 'Get Goals Aggregate (v1)'
      description: 'Deprecated. Use "Get Goals Aggregate (v1.1)" instead. Provides a list of all goals, type counts, goal comment counts, and employees shared with goals for the given employee. This version of the endpoint will not return any goals with milestones. Milestone functionality for this endpoint begins in version 1.2.'
      operationId: get-goals-aggregate-v1
      parameters:
        -
          name: employeeId
          in: path
          description: 'employeeId is the employee ID used to generate the aggregate information.'
          required: true
          schema:
            type: string
        -
          name: filter
          in: query
          description: 'Filter goals by status. Accepts filter IDs returned by the filters endpoint (e.g. status-inProgress). If omitted or invalid, defaults to the first available filter. The API accepts arbitrary strings and returns 200.'
          required: false
          schema:
            type: string
      responses:
        '200':
          description: 'The response content will be a JSON object with the requested information.'
          content:
            application/json:
              schema:
                title: GoalsAggregateV1
                properties:
                  canAlign: { description: 'The selected user can align goals with other users.', type: boolean, deprecated: true }
                  canCreateGoals: { description: 'The selected user can create a goal.', type: boolean, example: true, deprecated: true }
                  filters: { $ref: '#/components/schemas/GoalFiltersV1' }
                  selectedFilter: { description: 'The id of the current selected filter.', type: string, example: status-inProgress, deprecated: true }
                  goals: { description: 'All goals in selected filter.', type: array, items: { $ref: '#/components/schemas/TransformedApiGoal' }, deprecated: true }
                  persons: { description: 'A list of people with access to the goal.', type: array, items: { properties: { employeeId: { description: 'The id of this employee.', type: integer, example: 4353, deprecated: true }, userId: { description: 'The user id of the person if applicable.', type: integer, example: 45, deprecated: true }, displayFirstName: { description: 'First name of the person.', type: string, example: Tim, deprecated: true }, lastName: { description: 'Last name of the person.', type: string, example: Johnson, deprecated: true }, photoUrl: { description: 'url of the user profile image.', type: string, example: 'https://tim.johnson.jpg', deprecated: true } }, type: object }, deprecated: true }
                  comments: { description: 'A list of how many comments belong to each goal.', type: array, items: { properties: { goalId: { description: 'The goalId that the comments are linked to.', type: integer, example: 25, deprecated: true }, commentCount: { description: 'How many comments are linked to the goal', type: integer, example: 2, deprecated: true } }, type: object }, deprecated: true }
                type: object
        '403':
          description: 'The API user does not have permission to view goals for this employee.'
      deprecated: true
      security:
        -
          basic: []
        -
          oauth:
            - goal
  '/api/v1/performance/employees/{employeeId}/goals/canCreateGoals':
    get:
      tags:
        - Goals
        - 'Public API'
      summary: 'Get Goal Creation Permission'
      description: 'Determine if the API user has permission to create a goal for this employee.'
      operationId: get-goal-creation-permission
      parameters:
        -
          name: employeeId
          in: path
          description: 'employeeId is the employee ID with whom the goal is associated.'
          required: true
          schema:
            type: string
      responses:
        '200':
          description: 'Returns whether the API user can create a goal for this employee.'
          content:
            application/json:
              schema:
                title: CanCreateGoalsResponse
                properties:
                  canCreateGoals: { description: 'Whether the API user can create a goal for this employee.', type: boolean, example: true }
                type: object
        '400':
          description: 'The provided employeeId is invalid.'
        '403':
          description: 'The API user does not have permission to view goals for this employee.'
      security:
        -
          basic: []
        -
          oauth:
            - goal
  '/api/v1/performance/employees/{employeeId}/goals/shareOptions':
    get:
      tags:
        - Goals
        - 'Public API'
      summary: 'List Goal Sharing Options'
      description: 'Provides a list of employees with whom the specified employee\''s goals may be shared.'
      operationId: list-goal-share-options
      parameters:
        -
          name: employeeId
          in: path
          description: 'employeeId is the employee ID to get sharing options for.'
          required: true
          schema:
            type: string
        -
          name: search
          in: query
          description: 'The search term used to filter employees returned. Will search name, employee ID and email.'
          required: true
          schema:
            type: string
        -
          name: limit
          in: query
          description: 'Maximum number of employees to return. Defaults to 10, maximum 100.'
          schema:
            type: integer
            default: 10
            maximum: 100
            minimum: 1
      responses:
        '200':
          description: 'The response content will be a JSON document with a list of available employees with whom the goals can be shared.'
          content:
            application/json:
              schema:
                title: ShareOptionsResponse
                properties:
                  persons: { description: 'List of employees with whom goals can be shared.', type: array, items: { $ref: '#/components/schemas/PersonInfoApiTransformer' } }
                type: object
        '400':
          description: 'The search parameter is invalid or empty.'
        '403':
          description: 'Goals are not enabled, or the API user does not have permission to view sharing options for this employee.'
      security:
        -
          basic: []
        -
          oauth:
            - goal
  '/api/v1/performance/employees/{employeeId}/goals/{goalId}/comments':
    get:
      tags:
        - Goals
        - 'Public API'
      summary: 'List Goal Comments'
      description: 'Returns comments for a goal, including each comment''s ID, author, text, creation time, and edit/delete permissions. Use this endpoint before updating or deleting a comment when the user identifies a comment by text or says "my comment" rather than providing a comment ID.'
      operationId: list-goal-comments
      parameters:
        -
          name: employeeId
          in: path
          description: 'employeeId is the employee ID with whom the goal is associated.'
          required: true
          schema:
            type: string
        -
          name: goalId
          in: path
          description: 'goalId is the goal ID for the specified employee.'
          required: true
          schema:
            type: string
      responses:
        '200':
          description: 'Returns up to 50 comments for the specified goal, ordered by creation date.'
          content:
            application/json:
              schema:
                title: GoalCommentsResponse
                properties:
                  comments: { description: 'Array of comment objects for the goal.', type: array, items: { properties: { id: { description: 'The comment ID.', type: string }, authorUserId: { description: 'The user ID of the comment author.', type: integer }, createdAt: { description: 'ISO 8601 UTC timestamp when the comment was created.', type: string, example: '2025-04-10T15:36:38Z' }, text: { description: 'The text content of the comment.', type: string }, canEdit: { description: 'Whether the API user can edit this comment.', type: boolean }, canDelete: { description: 'Whether the API user can delete this comment.', type: boolean } }, type: object } }
                type: object
        '403':
          description: 'The API user does not have permission to view comments for this goal, or the goal does not belong to this employee.'
        '404':
          description: 'The specified goalId is invalid (non-positive integer).'
      security:
        -
          basic: []
        -
          oauth:
            - goal
    post:
      tags:
        - Goals
        - 'Public API'
      summary: 'Create Goal Comment'
      description: 'Creates a new comment on a goal. The goal must belong to the specified employee. Returns the newly created comment object including its assigned ID.'
      operationId: create-goal-comment
      parameters:
        -
          name: employeeId
          in: path
          description: 'employeeId is the employee ID with whom the goal is associated.'
          required: true
          schema:
            type: string
        -
          name: goalId
          in: path
          description: 'goalId is the goal ID for the specified employee.'
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - text
              properties:
                text:
                  description: 'The text content of the comment.'
                  type: string
              type: object
      responses:
        '201':
          description: 'A goal comment object with the new goal comment ID.'
          content:
            application/json:
              schema:
                title: GoalCommentResponse
                properties:
                  id: { description: 'The comment ID.', type: string }
                  authorUserId: { description: 'The user ID of the comment author.', type: integer }
                  createdAt: { description: 'ISO 8601 UTC timestamp when the comment was created.', type: string, example: '2025-04-10T15:36:38Z' }
                  text: { description: 'The text content of the comment.', type: string }
                  canEdit: { description: 'Whether the API user can edit this comment.', type: boolean }
                  canDelete: { description: 'Whether the API user can delete this comment.', type: boolean }
                type: object
        '400':
          description: 'The goal does not belong to the specified employee, or the comment text is blank.'
          content:
            application/json: {  }
        '403':
          description: 'If the API user does not have permission to add a comment to the specified goal.'
          content:
            application/json: {  }
        '404':
          description: 'The specified goalId was not found.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - goal.write
  '/api/v1/performance/employees/{employeeId}/goals/{goalId}/comments/{commentId}':
    put:
      tags:
        - Goals
        - 'Public API'
      summary: 'Update Goal Comment'
      description: "Updates the text of an existing goal comment. The comment must belong to the specified goal, and the goal must belong to the specified employee. Returns the updated comment object. If the user does not provide a comment ID, first call `list-goal-comments` and select a single editable comment (`canEdit` is true) matching the user's description. If multiple comments match or none are editable, do not update; ask for clarification."
      operationId: update-goal-comment
      parameters:
        -
          name: employeeId
          in: path
          description: 'employeeId is the employee ID with whom the goal is associated.'
          required: true
          schema:
            type: string
        -
          name: goalId
          in: path
          description: 'goalId is the goal ID for the specified employee.'
          required: true
          schema:
            type: string
        -
          name: commentId
          in: path
          description: 'commentId is the comment ID for the specified goal.'
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - text
              properties:
                text:
                  description: 'The updated text content of the comment.'
                  type: string
              type: object
      responses:
        '200':
          description: 'A successful response indicates that all the requested changes were made. The content of the response will be the goal comment response object for the specified commentId.'
          content:
            application/json:
              schema:
                title: GoalCommentResponse
                properties:
                  id: { description: 'The comment ID.', type: string }
                  authorUserId: { description: 'The user ID of the comment author.', type: integer }
                  createdAt: { description: 'ISO 8601 UTC timestamp when the comment was created.', type: string, example: '2025-04-10T15:36:38Z' }
                  text: { description: 'The text content of the comment.', type: string }
                  canEdit: { description: 'Whether the API user can edit this comment.', type: boolean }
                  canDelete: { description: 'Whether the API user can delete this comment.', type: boolean }
                type: object
        '400':
          description: 'The goal does not belong to the specified employee, or the comment text is blank.'
          content:
            application/json: {  }
        '403':
          description: 'Goal is not editable or insufficient permissions.'
          content:
            application/json: {  }
        '404':
          description: 'The goal specified by the given goalId was not found.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - goal.write
    delete:
      tags:
        - Goals
        - 'Public API'
      summary: 'Delete Goal Comment'
      description: "Deletes a goal comment. The comment must belong to the specified goal, and the goal must belong to the specified employee. Returns 204 with no response body on success. If the user does not provide a comment ID, first call `list-goal-comments` and select a single deletable comment (`canDelete` is true) matching the user's description. If multiple comments match or none are deletable, do not delete; ask for clarification."
      operationId: delete-goal-comment
      parameters:
        -
          name: employeeId
          in: path
          description: 'employeeId is the employee ID with whom the goal is associated.'
          required: true
          schema:
            type: string
        -
          name: goalId
          in: path
          description: 'goalId is the goal ID for the specified employee.'
          required: true
          schema:
            type: string
        -
          name: commentId
          in: path
          description: 'commentId is the ID of a specific comment for the specified goal.'
          required: true
          schema:
            type: string
      responses:
        '204':
          description: 'The comment was successfully deleted. No response body is returned.'
        '400':
          description: 'The goal does not belong to the specified employee.'
          content:
            application/json: {  }
        '403':
          description: 'Goal is not editable or insufficient permissions.'
          content:
            application/json: {  }
        '404':
          description: 'The goal specified by the given goalId was not found.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - goal.write
  '/api/v1/performance/employees/{employeeId}/goals/{goalId}/aggregate':
    get:
      tags:
        - Goals
        - 'Public API'
      summary: 'Get Goal Aggregate'
      description: 'Returns a single goal with its comments, alignment options, and a list of all persons who are either shared on the goal or have commented on it. Useful for rendering a full goal detail view in a single request.'
      operationId: get-goal-aggregate
      parameters:
        -
          name: employeeId
          in: path
          description: 'employeeId is the employee ID with whom the goal is associated.'
          required: true
          schema:
            type: string
        -
          name: goalId
          in: path
          description: 'goalId is the Goal ID used to generate the aggregate information.'
          required: true
          schema:
            type: string
      responses:
        '200':
          description: 'The response content will be a JSON object with the requested information.'
          content:
            application/json:
              schema:
                title: GoalAggregate
                properties:
                  goal: { $ref: '#/components/schemas/TransformedApiGoal' }
                  canAlign: { description: 'The selected user can align goals with other users.', type: boolean }
                  canCreateGoals: { description: 'The selected user can create a goal.', type: boolean, example: true }
                  alignsWithOptions: { description: 'All possible goals that this goal could be aligned with.', type: array, items: { properties: { id: { description: 'Id of the goal that the selected goal could be aligned with.', type: integer, example: 8 }, title: { description: 'Title of the goal that the selected goal could be aligned with.', type: string, example: 'Get all team goals for January complete.' } }, type: object } }
                  comments: { description: 'Comments linked to selected goal.', type: array, items: { properties: { id: { description: 'Id of the comment.', type: string, example: '2' }, authorUserId: { description: 'Id of the author of the comment.', type: integer, example: 1534 }, createdAt: { description: 'ISO 8601 UTC timestamp when the comment was created.', type: string, example: '2022-05-23T15:52:42Z' }, text: { description: 'The actual text of the comment.', type: string, example: 'This goal is taking longer than I thought.' }, canEdit: { description: 'Can the comment be edited.', type: boolean }, canDelete: { description: 'Can the comment be deleted.', type: boolean } }, type: object } }
                  persons: { description: 'A list of people with access to the goal.', type: array, items: { properties: { employeeId: { description: 'The id of this employee.', type: integer, example: 4353 }, userId: { description: 'The user id of the person if applicable.', type: integer, example: 45 }, displayFirstName: { description: 'First name of the person.', type: string, example: Tim }, lastName: { description: 'Last name of the person.', type: string, example: Johnson }, photoUrl: { description: 'url of the user profile image.', type: string, example: 'https://tim.johnson.jpg' } }, type: object } }
                type: object
        '403':
          description: 'The API user does not have permission to view this goal.'
        '404':
          description: 'The specified goalId was not found.'
      security:
        -
          basic: []
        -
          oauth:
            - goal
  '/api/v1/performance/employees/{employeeId}/goals/alignmentOptions':
    get:
      tags:
        - Goals
        - 'Public API'
      summary: 'Get Alignable Goal Options'
      description: 'Returns goals that can be used as alignment targets. When a `goalId` query parameter is provided, the currently aligned goal is included in the results even if it would otherwise be excluded. When `goalId` is omitted, returns alignment options for the API user. This endpoint is permission-sensitive: the caller may be able to view a goal through `list-goals` or `get-goal-aggregate` but still receive 403 here if they lack permission to view alignment options for the selected goal. Treat 403 as "alignment options are permission-restricted," not as proof that the goal ID is invalid.'
      operationId: get-alignable-goal-options
      parameters:
        -
          name: employeeId
          in: path
          description: 'employeeId is the employee ID to get alignable goal options for.'
          required: true
          schema:
            type: string
        -
          name: goalId
          in: query
          description: 'Optional goal ID whose current alignment should be included in the results. Only pass a goal ID that belongs to the specified employee. If this endpoint returns 403 for an otherwise visible goal, report that alignment options are restricted by permissions rather than treating the goal ID as invalid.'
          required: false
          schema:
            type: integer
      responses:
        '200':
          description: 'The response content will be a JSON document with a list of goals that are available alignment options.'
          content:
            application/json:
              schema:
                title: AlignmentOptionsResponse
                properties:
                  alignsWithOptions: { description: 'Array of goals available as alignment targets for the employee.', type: array, items: { properties: { id: { description: 'The goal ID.', type: string }, title: { description: 'The display title of the goal.', type: string } }, type: object } }
                type: object
        '400':
          description: 'The provided goalId does not belong to the specified employee.'
        '403':
          description: 'The employeeId resolves to 0 (invalid path segment), or the API user lacks permission to view the specified goal.'
        '404':
          description: 'The provided goalId is zero or non-positive after integer cast (e.g. goalId=0, goalId=-1, or a non-numeric value such as goalId=abc).'
      security:
        -
          basic: []
        -
          oauth:
            - goal
  '/api/v1/performance/employees/{employeeId}/goals/{goalId}/close':
    post:
      tags:
        - Goals
        - 'Public API'
      summary: 'Close Goal'
      description: "Closes a goal, moving it to the closed status. An optional comment may be included in the request body to record a note at closing time. Returns the updated goal object. Validate the result using the returned `status`, `percentComplete`, and `completionDate` fields; do not rely solely on `lastChangedDateTime` as the confirmation timestamp, because it may not match the HTTP response time or may reflect BambooHR's internal timezone/update semantics. Note: Cascading goals with visible children cannot be closed."
      operationId: close-goal
      parameters:
        -
          name: employeeId
          in: path
          description: 'employeeId is the employee ID with whom the goal is associated.'
          required: true
          schema:
            type: string
        -
          name: goalId
          in: path
          description: 'goalId is the goal ID for the specified employee.'
          required: true
          schema:
            type: string
      requestBody:
        description: 'An optional comment to record when closing the goal.'
        required: false
        content:
          application/json:
            schema:
              properties:
                comment:
                  description: 'Optional comment to record when closing the goal.'
                  example: 'Closing this goal as the deadline has passed.'
                  oneOf: [{ type: string }, { type: 'null' }]
              type: object
      responses:
        '201':
          description: 'The goal was successfully closed. The response body contains the updated goal object.'
          content:
            application/json:
              schema:
                title: GoalResponse
                properties:
                  goal: { $ref: '#/components/schemas/TransformedApiGoal' }
                type: object
        '400':
          description: 'The request is invalid. Possible causes: the specified employee is not associated with this goal, or the goal is a cascading goal with visible children and cannot be closed.'
          content:
            application/json: {  }
        '403':
          description: 'Goal is not editable or insufficient permissions.'
          content:
            application/json: {  }
        '404':
          description: 'The goal specified by the given goalId was not found.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - goal.write
  '/api/v1/performance/employees/{employeeId}/goals/{goalId}/reopen':
    post:
      tags:
        - Goals
        - 'Public API'
      summary: 'Reopen Goal'
      description: "Reopens a closed goal, returning it to the in-progress status. Returns the updated goal object. Validate the result using the returned `status` and `percentComplete` fields; do not rely solely on `lastChangedDateTime` as the confirmation timestamp, because it may not match the HTTP response time or may reflect BambooHR's internal timezone/update semantics."
      operationId: reopen-goal
      parameters:
        -
          name: employeeId
          in: path
          description: 'employeeId is the employee ID with whom the goal is associated.'
          required: true
          schema:
            type: string
        -
          name: goalId
          in: path
          description: 'goalId is the goal ID for the specified employee.'
          required: true
          schema:
            type: string
      responses:
        '201':
          description: 'The goal was successfully reopened. The response body contains the updated goal object.'
          content:
            application/json:
              schema:
                title: GoalResponse
                properties:
                  goal: { $ref: '#/components/schemas/TransformedApiGoal' }
                type: object
        '400':
          description: 'The request is invalid. The specified employee is not associated with this goal.'
          content:
            application/json: {  }
        '403':
          description: 'Goal is not editable or insufficient permissions.'
          content:
            application/json: {  }
        '404':
          description: 'The goal specified by the given goalId was not found.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - goal.write
  '/api/v1_1/performance/employees/{employeeId}/goals/filters':
    get:
      tags:
        - Goals
        - 'Public API'
      summary: 'Get Goal Filters (v1.1)'
      description: 'Deprecated. Use "Get Goal Filters (v1.2)" instead. Get the number of goals per status for an employee. Note: Compared to "Get Goal Filters (v1)", this version includes actions.'
      operationId: get-goals-filters-v1.1
      parameters:
        -
          name: employeeId
          in: path
          description: 'employeeId is the employee ID to whom the goals are assigned.'
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Contains the map of goal statuses, their counts, and available actions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GoalFiltersV1_1'
        '403':
          description: 'The API user does not have permission to view goals for this employee.'
        '500':
          description: 'Something went wrong fetching filters.'
      deprecated: true
      security:
        -
          basic: []
        -
          oauth:
            - goal
  '/api/v1_1/performance/employees/{employeeId}/goals/aggregate':
    get:
      tags:
        - Goals
        - 'Public API'
      summary: 'Get Goals Aggregate (v1.1)'
      description: 'Deprecated. Use "Get Goals Aggregate (v1.2)" instead. Provides a list of all goals, type counts, filter actions, goal comment counts, and employees shared with goals for the given employee. Note: Compared to "Get Goals Aggregate (v1)", this version returns goals in the closed filter and provides filter actions for each filter. This version of the endpoint will not return any goals with milestones. Milestone functionality for this endpoint begins in version 1.2.'
      operationId: get-goals-aggregate-v1.1
      parameters:
        -
          name: employeeId
          in: path
          description: 'employeeId is the employee ID used to generate the aggregate information.'
          required: true
          schema:
            type: string
        -
          name: filter
          in: query
          description: 'Filter goals by status. Accepts filter IDs returned by the filters endpoint (e.g. status-inProgress). If omitted or invalid, defaults to the first available filter. The API accepts arbitrary strings and returns 200.'
          required: false
          schema:
            type: string
      responses:
        '200':
          description: 'The response content will be a JSON object with the requested information.'
          content:
            application/json:
              schema:
                title: GoalsAggregateV1_1
                properties:
                  canAlign: { description: 'The selected user can align goals with other users.', type: boolean, example: true, deprecated: true }
                  canCreateGoals: { description: 'The selected user can create a goal.', type: boolean, example: true, deprecated: true }
                  filters: { $ref: '#/components/schemas/GoalFiltersV1_1' }
                  selectedFilter: { description: 'The id of the current selected filter.', type: string, example: status-inProgress, deprecated: true }
                  goals: { description: 'All goals in selected filter.', type: array, items: { $ref: '#/components/schemas/TransformedApiGoal' }, deprecated: true }
                  persons: { description: 'A list of people with access to the goal.', type: array, items: { properties: { employeeId: { description: 'The id of this employee.', type: integer, example: 4353, deprecated: true }, userId: { description: 'The user id of the person if applicable.', type: integer, example: 45, deprecated: true }, displayFirstName: { description: 'First name of the person.', type: string, example: Tim, deprecated: true }, lastName: { description: 'Last name of the person.', type: string, example: Johnson, deprecated: true }, photoUrl: { description: 'url of the user profile image.', type: string, example: 'https://tim.johnson.jpg', deprecated: true } }, type: object }, deprecated: true }
                  comments: { description: 'A list of how many comments belong to each goal.', type: array, items: { properties: { goalId: { description: 'The goalId that the comments are linked to.', type: string, example: '25', deprecated: true }, commentCount: { description: 'How many comments are linked to the goal', type: integer, example: 2, deprecated: true } }, type: object }, deprecated: true }
                type: object
        '403':
          description: 'The API user does not have permission to view goals for this employee.'
      deprecated: true
      security:
        -
          basic: []
        -
          oauth:
            - goal
  '/api/v1_1/performance/employees/{employeeId}/goals/{goalId}':
    put:
      tags:
        - Goals
        - 'Public API'
      summary: 'Update Goal (v1.1)'
      description: 'Update a goal''s top-level fields and optionally add or delete milestones. Milestone handling is not a full replace or upsert: objects passed in `milestones` are always added as new milestones, even if their titles match existing milestones. To keep existing milestones unchanged while editing title, description, due date, sharing, or alignment, omit the `milestones` field. To remove milestones, pass their IDs in `deletedMilestoneIds`. There is no field for editing an existing milestone title in place. Note: Compared to "Update Goal (v1)", this version adds milestone updates.'
      operationId: update-goal-v1.1
      parameters:
        -
          name: employeeId
          in: path
          description: 'employeeId is the employee ID with whom the goal is associated.'
          required: true
          schema:
            type: integer
        -
          name: goalId
          in: path
          description: 'goalId is the goal ID for the specified employee.'
          required: true
          schema:
            type: integer
      requestBody:
        description: 'Required fields: `title`, `sharedWithEmployeeIds`, and `dueDate`. Omit optional fields that should remain unchanged. In particular, omit `milestones` unless adding new milestones; included milestones are appended, not reconciled or replaced.'
        required: true
        content:
          application/json:
            schema:
              required:
                - title
                - dueDate
                - sharedWithEmployeeIds
              properties:
                title:
                  description: 'The title of the goal'
                  type: string
                  example: 'Edited Goal With Milestones'
                description:
                  description: 'A detailed description of the goal'
                  type: string
                  example: 'Lets go!'
                dueDate:
                  description: 'The due date for the goal in YYYY-MM-DD format'
                  type: string
                  format: date
                  example: '2024-03-17'
                percentComplete:
                  description: 'The percentage of completion for the goal (0-100). Defaults to 0 if omitted. Ignored when milestonesEnabled is true.'
                  type: integer
                  maximum: 100
                  minimum: 0
                completionDate:
                  description: 'The date when the goal was completed in YYYY-MM-DD format. Only valid when percentComplete is 100; providing this field with any other percentComplete value will result in an error. Ignored when milestonesEnabled is true.'
                  oneOf: [{ type: string, format: date }, { type: 'null' }]
                sharedWithEmployeeIds:
                  description: 'List of employee IDs with whom the goal is shared. Must include the employee ID of the goal owner.'
                  type: array
                  items: { type: integer }
                  example: [1234, 5678]
                alignsWithOptionId:
                  description: 'ID of the option this goal aligns with'
                  oneOf: [{ type: integer }, { type: 'null' }]
                milestonesEnabled:
                  description: 'Flag indicating whether milestones are enabled for this goal'
                  type: boolean
                  example: true
                deletedMilestoneIds:
                  description: 'List of milestone IDs to be deleted from the goal'
                  type: array
                  items: { type: integer }
                  example: [490]
                milestones:
                  description: 'Optional. New milestones to add to the goal. Each object in this array creates a new milestone — even if its title matches an existing milestone. Do not include existing milestones here unless you intentionally want duplicates. To remove milestones, use `deletedMilestoneIds`.'
                  type: array
                  items: { properties: { title: { description: 'The title of the milestone', type: string } }, type: object }
              type: object
      responses:
        '200':
          description: 'A successful response indicates that all the requested changes were made. The content of the response will be the goal response object for the specified goalId.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransformedApiEmployeeGoalDetails'
        '400':
          description: 'The request is invalid. Possible causes: sharedWithEmployeeIds is not an array or missing the goal owner, percentComplete is out of range (0–100), the goal has milestones and percentComplete cannot be manually updated, or the goal is a cascading goal (not supported by this endpoint).'
          content:
            application/json: {  }
        '403':
          description: 'Goal is not editable or insufficient permissions.'
          content:
            application/json: {  }
        '404':
          description: 'The goal specified by the given goalId was not found.'
          content:
            application/json: {  }
        '500':
          description: 'Something went wrong editing your goal.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - goal.write
  '/api/v1_2/performance/employees/{employeeId}/goals/filters':
    get:
      tags:
        - Goals
        - 'Public API'
      summary: 'Get Goal Filters (v1.2)'
      description: 'Get the number of goals per status for an employee, including goals with milestones. Note: Compared to "Get Goal Filters (v1.1)", this version returns goals with milestones.'
      operationId: get-goals-filters-v1.2
      parameters:
        -
          name: employeeId
          in: path
          description: 'employeeId is the employee ID to whom the goals are assigned.'
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Contains the map of goal statuses, their counts, and available actions'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/GoalFiltersV1_1'
        '403':
          description: 'The API user does not have permission to view goals for this employee.'
        '500':
          description: 'Something went wrong fetching filters.'
      security:
        -
          basic: []
        -
          oauth:
            - goal
  '/api/v1_2/performance/employees/{employeeId}/goals/aggregate':
    get:
      tags:
        - Goals
        - 'Public API'
      summary: 'Get Goals Aggregate (v1.2)'
      description: 'Provides a goals dashboard for an employee: goals matching the selected filter, status filter counts/actions, comment counts, and people shared with or commenting on those goals. This v1.2 endpoint includes milestone-based goals in the returned goal list. Use the optional `filter` query parameter to control which statuses are included; use `status-all` when the user asks for a complete dashboard across active, completed, and closed goals. Note: Compared to "Get Goals Aggregate (v1.1)", this version returns goals that contain milestones.'
      operationId: get-goals-aggregate-v1.2
      parameters:
        -
          name: employeeId
          in: path
          description: 'employeeId is the employee ID used to generate the aggregate information.'
          required: true
          schema:
            type: integer
        -
          name: filter
          in: query
          description: "Optional status filter for the goals collection in the aggregate response. Use `status-all` for a full dashboard across statuses; otherwise pass `status-inProgress`, `status-completed`, or `status-closed`. If omitted or invalid, defaults to the first available filter. The response's `filters` counts may include statuses beyond the selected goal list."
          required: false
          schema:
            type: string
      responses:
        '200':
          description: 'The response content will be a JSON object with the requested information.'
          content:
            application/json:
              schema:
                title: GoalsAggregateV1_2
                properties:
                  canAlign: { description: 'The selected user can align goals with other users.', type: boolean, example: true }
                  canCreateGoals: { description: 'The selected user can create a goal.', type: boolean, example: true }
                  filters: { $ref: '#/components/schemas/GoalFiltersV1_1' }
                  selectedFilter: { description: 'The id of the current selected filter.', type: string, example: status-inProgress }
                  goals: { description: 'All goals in selected filter.', type: array, items: { $ref: '#/components/schemas/TransformedApiGoal' } }
                  persons: { description: 'A list of people with access to the goal.', type: array, items: { properties: { employeeId: { description: 'The id of this employee.', type: integer, example: 4353 }, userId: { description: 'The user id of the person if applicable.', type: integer, example: 45 }, displayFirstName: { description: 'First name of the person.', type: string, example: Tim }, lastName: { description: 'Last name of the person.', type: string, example: Johnson }, photoUrl: { description: 'url of the user profile image.', type: string, example: 'https://tim.johnson.jpg' } }, type: object } }
                  comments: { description: 'A list of how many comments belong to each goal.', type: array, items: { properties: { goalId: { description: 'The goalId that the comments are linked to.', type: string, example: '25' }, commentCount: { description: 'How many comments are linked to the goal', type: integer, example: 2 } }, type: object } }
                type: object
        '403':
          description: 'The API user does not have permission to view goals for this employee.'
      security:
        -
          basic: []
        -
          oauth:
            - goal
  '/api/v1/timetracking/record/{id}':
    get:
      tags:
        - Hours
        - 'Public API'
      summary: 'Get Time Tracking Record'
      description: 'Retrieves a single time tracking hour record by its ID. Returns the full record details including hours, date, employee, project, task, and shift differential information. The `project` and `shiftDifferential` fields are null when not applicable. For historical compatibility, missing records may surface as an empty/null payload rather than a strict not-found response.'
      operationId: get-time-tracking-record
      parameters:
        -
          name: id
          in: path
          description: 'The time tracking record ID used to originally create the record.'
          required: true
          schema:
            type: string
      responses:
        '200':
          description: 'Successfully retrieved the time tracking record.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimeTrackingRecordSchema'
        '400':
          description: 'Invalid or missing argument.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden. Insufficient permissions to view this record.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - time_tracking
  /api/v1/timetracking/add:
    post:
      tags:
        - 'Public API'
        - Hours
      summary: 'Create Hour Record'
      description: 'Adds a single hour record. Use this endpoint when creating one record at a time. For bulk imports, use create-or-update-time-tracking-hour-records.'
      operationId: create-time-tracking-hour-record
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TimeTrackingRecord'
      responses:
        '201':
          description: 'Record created. Returns the new time tracking ID.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimeTrackingIdResponseSchema'
        '400':
          description: 'If any required field is missing, referenced IDs are invalid, or the posted JSON is invalid.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden. Insufficient user permissions.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - time_tracking.write
  /api/v1/timetracking/record:
    post:
      tags:
        - 'Public API'
        - Hours
      summary: 'Create or Update Hour Records'
      description: "Bulk add/edit hour records. The endpoint can return HTTP 201 even when individual items fail validation; inspect each item's `success` flag and per-item `response.message` for partial failures."
      operationId: create-or-update-time-tracking-hour-records
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TimeTrackingRecordBulk'
      responses:
        '201':
          description: "Returns an array of per-item results. Each item has a `success` boolean and a `response` object containing the resulting `id` on success, or a `message` describing the failure. Note: HTTP 201 is returned even when individual items fail; inspect each item's `success` field for partial failures."
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimeTrackingBulkResponseSchema'
        '403':
          description: 'Forbidden. Insufficient user permissions.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - time_tracking.write
  /api/v1/timetracking/adjust:
    put:
      tags:
        - 'Public API'
        - Hours
      summary: 'Update Hour Record'
      description: 'Edits an existing hour record by `timeTrackingId`.'
      operationId: update-time-tracking-record
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AdjustTimeTrackingRequestSchema'
      responses:
        '201':
          description: 'Record updated. Returns the time tracking ID.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimeTrackingIdResponseSchema'
        '400':
          description: 'If any required field is missing, referenced IDs are invalid, or the posted JSON is invalid.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden. Insufficient user permissions.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - time_tracking.write
  '/api/v1/timetracking/delete/{id}':
    delete:
      tags:
        - 'Public API'
        - Hours
      summary: 'Delete Hour Record'
      description: 'Deletes an hour record by `timeTrackingId` (`id` path parameter). This removes all stored revisions associated with that logical time tracking record. Not-found and invalid-id cases are currently returned as a 400 invalid-argument response for backward compatibility.'
      operationId: delete-time-tracking-hour-record
      parameters:
        -
          name: id
          in: path
          description: 'The time tracking id is the id that was used to track the record up to 36 characters in length. (i.e. UUID).'
          required: true
          schema:
            type: string
      responses:
        '200':
          description: 'Record removed.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimeTrackingDeleteResponseSchema'
        '400':
          description: 'If the time tracking ID cannot be found.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Forbidden. Insufficient permissions to delete this record.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - time_tracking.write
  /api/v1/compensation/total_rewards/employees:
    post:
      tags:
        - 'Total Rewards'
        - 'Public API'
      summary: 'Add Employees to Total Rewards'
      description: 'Add employees to Total Rewards. Each employee will have a Total Rewards profile created and will be notified if they are active users.'
      operationId: add-total-rewards-employees
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - employeeIds
              properties:
                employeeIds:
                  description: 'List of employee IDs to add to Total Rewards.'
                  type: array
                  items: { type: integer }
              type: object
      responses:
        '200':
          description: 'Employees successfully added to Total Rewards.'
        '400':
          description: 'Invalid request (e.g. missing or empty employeeIds).'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'The authenticated caller lacks permission to manage Total Rewards employees.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Unexpected server error.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - total_rewards.write
      x-description: 'Add Employees to Total Rewards'
    delete:
      tags:
        - 'Total Rewards'
        - 'Public API'
      summary: 'Remove Employees from Total Rewards'
      description: 'Remove employees from Total Rewards. Their Total Rewards profiles will be deleted.'
      operationId: remove-total-rewards-employees
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - employeeIds
              properties:
                employeeIds:
                  description: 'List of employee IDs to remove from Total Rewards.'
                  type: array
                  items: { type: integer }
              type: object
      responses:
        '200':
          description: 'Employees successfully removed from Total Rewards.'
        '400':
          description: 'Invalid request (e.g. missing or empty employeeIds).'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'The authenticated caller lacks permission to manage Total Rewards employees.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Unexpected server error.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - total_rewards.write
      x-description: 'Remove Employees from Total Rewards'
  '/api/v1/compensation/total_rewards/onboarding/{stepName}':
    put:
      tags:
        - 'Total Rewards'
        - 'Public API'
      summary: 'Set Total Rewards Onboarding Step Status'
      description: 'Set a Total Rewards onboarding step to completed or incomplete. Valid step names are defined by the Total Rewards onboarding configuration.'
      operationId: set-total-rewards-onboarding-step
      parameters:
        -
          name: stepName
          in: path
          description: 'Name of the onboarding step.'
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - completed
              properties:
                completed:
                  description: 'Whether the step is completed.'
                  type: boolean
              type: object
      responses:
        '200':
          description: 'Onboarding step status updated successfully.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TotalRewardsOnboardingStep'
        '400':
          description: 'Invalid argument (e.g. invalid step name or non-boolean completed value).'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'The authenticated caller lacks permission to manage Total Rewards onboarding.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Unexpected server error.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - total_rewards.write
      x-description: 'Set Total Rewards Onboarding Step Status'
  /api/v1/compensation/total_rewards/custom_disclaimer:
    put:
      tags:
        - 'Total Rewards'
        - 'Public API'
      summary: 'Set Total Rewards Custom Disclaimer'
      description: 'Set the company-wide Total Rewards custom disclaimer text shown on employee statements.'
      operationId: set-total-rewards-custom-disclaimer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - customDisclaimer
              properties:
                customDisclaimer:
                  description: 'Custom disclaimer text.'
                  type: string
              type: object
      responses:
        '200':
          description: 'Custom disclaimer updated successfully.'
        '403':
          description: 'The authenticated caller lacks permission to manage Total Rewards settings.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Unexpected server error.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - total_rewards.write
      x-description: 'Set Total Rewards Custom Disclaimer'
    delete:
      tags:
        - 'Total Rewards'
        - 'Public API'
      summary: 'Remove Total Rewards Custom Disclaimer'
      description: 'Remove the company-wide Total Rewards custom disclaimer. After removal, the default disclaimer will be shown on employee statements.'
      operationId: remove-total-rewards-custom-disclaimer
      responses:
        '200':
          description: 'Custom disclaimer removed successfully.'
        '403':
          description: 'The authenticated caller lacks permission to manage Total Rewards settings.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Unexpected server error.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - total_rewards.write
      x-description: 'Remove Total Rewards Custom Disclaimer'
  '/api/v1/compensation/total_rewards/{employeeId}/statement':
    get:
      tags:
        - 'Total Rewards'
        - 'Public API'
      summary: 'Get Total Rewards Statement'
      description: 'Returns the full Total Rewards statement for the given employee, including compensation, benefits, bonuses, equity, reimbursements, and time-off data.'
      operationId: get-total-rewards-statement
      parameters:
        -
          name: employeeId
          in: path
          description: 'The ID of the employee.'
          required: true
          schema:
            type: string
      responses:
        '200':
          description: 'Total Rewards statement for the employee.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TotalRewards-EmployeeStatement'
        '403':
          description: "The authenticated caller lacks permission to view this employee's Total Rewards statement."
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Unexpected server error.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - total_rewards
      x-description: 'Get Total Rewards Statement'
  /api/v1/meta/provinces:
    get:
      tags:
        - 'Account Information'
        - 'Public API'
      summary: 'Get All Provinces'
      description: 'Returns a flat list of all states and provinces across every country. Each entry includes a numeric ID, the countryId it belongs to, an abbreviation label (e.g. "UT"), an ISO 3166-2 code (e.g. "US-UT"), and a full name. Use the countryId field to filter client-side.'
      operationId: get-all-provinces
      responses:
        '200':
          description: 'Flat array of all states/provinces across all countries.'
          content:
            application/json:
              schema:
                title: AllProvincesResponse
                type: array
                items:
                  title: ProvinceItem
                  properties: { id: { description: 'State/province ID', type: integer }, countryId: { description: 'ID of the country this state/province belongs to', type: string }, label: { description: 'State/province abbreviation (e.g. "UT")', type: string }, iso: { description: 'ISO 3166-2 code (e.g. "US-UT")', type: string }, name: { description: 'Full name of the state/province', type: string } }
                  type: object
      security:
        -
          oauth:
            - meta
  '/api/v1/meta/provinces/{countryId}':
    get:
      tags:
        - 'Account Information'
        - 'Public API'
      summary: 'List states and provinces for a country by Country ID'
      description: 'Returns the list of states or provinces for the specified country, sorted alphabetically by abbreviation (`options[].label`). Each item follows StateProvinceSchema: `label` is the subdivision abbreviation (e.g. "UT"), not the full name; `name` is the full subdivision name; `iso` is the ISO 3166-2 code (e.g. "US-UT"). Use a `countryId` from `GET /api/v1/meta/countries/options` (the `id` field on the row for the country) so it matches the countries list.'
      operationId: get-states-by-country-id
      parameters:
        -
          name: countryId
          in: path
          description: 'Numeric id of the country, taken from the countries options list. Use the `id` of the target country from `GET /api/v1/meta/countries/options`.'
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'List of states/provinces. The `options` array items match StateProvinceSchema; `label` is always the subdivision abbreviation used for sorting.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/StateProvinceResponseSchema'
        '400':
          description: 'The provided country ID is invalid.'
      security:
        -
          basic: []
        -
          oauth:
            - field
  /api/v1/meta/currency/types:
    get:
      tags:
        - 'Account Information'
        - 'Public API'
      summary: 'Get all currency types'
      description: 'Returns a JSON array of supported currency catalog entries. Each object includes `id`, `code`, `name`, `symbol` (display symbol), and `symbolPosition`—an integer discriminator: `0` = symbol before the amount (prefix) and `1` = after the amount (postfix), matching the values returned in the JSON body.'
      operationId: get-all-currency-types
      responses:
        '200':
          description: '200 OK with a JSON array of currency objects, each with `id`, `code`, `name`, `symbol`, and `symbolPosition`.'
          content:
            application/json:
              schema:
                title: MetaCurrencyTypesListResponse
                type: array
                items:
                  title: MetaCurrencyTypeItem
                  properties: { id: { description: 'Internal currency id', type: integer }, code: { description: 'ISO 4217-style currency code (e.g. USD, EUR)', type: string }, name: { description: 'Human-readable currency name (HTML entities decoded in the response)', type: string }, symbol: { description: 'Display symbol for the currency in UI', type: string }, symbolPosition: { description: 'Placement of the symbol relative to the amount: 0 = prefix, 1 = postfix (this is a numeric code, not a string label).', type: integer, enum: [0, 1] } }
                  type: object
      security:
        -
          basic: []
  /api/v1/meta/company:
    get:
      tags:
        - 'Account Information'
        - 'Public API'
      summary: 'Get company properties'
      description: 'Get company properties including ID, name, domain, and base API URL. Provides essential company metadata for API access.'
      operationId: get-meta-company
      responses:
        '200':
          description: 'Company id, name, domain, and baseApiUrl gateway URL.'
          content:
            application/json:
              schema:
                title: MetaCompanyPropertiesResponse
                properties:
                  id: { description: 'Bamboo company identifier (numeric).', type: integer }
                  name: { description: 'Company display name.', type: string }
                  domain: { description: 'Company’s Bamboo subdomain (same value used in the `baseApiUrl` path segment after `gateway.php/`)', type: string }
                  baseApiUrl: { description: 'Full HTTPS URL of the public API gateway for this company, typically `https://` + the data-center–specific API host for this tenant + ` /api/gateway.php/` + the company’s `domain` from this response. This is the entry point the integrator uses for legacy gateway-style API traffic, not a generic “base URL” name alone.', type: string }
                type: object
      security:
        -
          oauth:
            - 'company:info'
  /api/v1/meta/countries/options:
    get:
      tags:
        - 'Account Information'
        - 'Public API'
      summary: 'Get Countries'
      description: 'Returns a JSON array of every country in the catalog, or a single country object when `isoCode` is supplied. Each element has `id` (Country ID), `name` (Country Name), and `isoCode` (ISO 3166-1 alpha-2 code or null when unset).'
      operationId: get-countries-options
      parameters:
        -
          name: isoCode
          in: query
          description: 'ISO 3166-1 alpha-2 country code (exactly two letters). When present, returns the matching country as a single object instead of the full array.'
          required: false
          schema:
            type: string
            maxLength: 2
            minLength: 2
            pattern: '^[A-Za-z]{2}$'
      responses:
        '200':
          description: 'When `isoCode` is omitted: a JSON array of all country objects. When `isoCode` is provided: the matching country as a single object.'
          content:
            application/json:
              schema:
                title: CountriesOptionsResponse
                oneOf:
                  - { $ref: '#/components/schemas/CountriesResponseSchema' }
                  - { $ref: '#/components/schemas/CountrySchema' }
        '404':
          description: 'No country found for the given `isoCode`.'
        '422':
          description: '`isoCode` is present but malformed (not exactly two alpha characters).'
      security:
        -
          basic: []
        -
          oauth:
            - field
        -
          oauth:
            - meta
  /api/v1/meta/industries:
    get:
      tags:
        - 'Public API'
      summary: 'Get Industries'
      description: 'Returns the complete list of industry reference data including codes. Use the returned IDs when creating or updating company profiles.'
      operationId: get-industries
      responses:
        '200':
          description: 'List of all industries.'
          content:
            application/json:
              schema:
                title: IndustriesListResponse
                type: array
                items:
                  title: Industry
                  properties: { id: { description: 'Industry ID', type: integer }, name: { description: 'Industry name', type: string } }
                  type: object
        '401':
          description: Unauthorized
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: Forbidden
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - meta
  '/api/v1/meta/countries/{id}':
    get:
      tags:
        - 'Public API'
      summary: 'Get country by ID'
      description: 'Returns a single country by catalog id. Response fields match each element of GET /api/v1/meta/countries/options (`id`, `name`, `isoCode` and nullability).'
      operationId: get-meta-country-by-id
      parameters:
        -
          name: id
          in: path
          description: 'Country id from GET /api/v1/meta/countries/options (`id` on the desired row).'
          required: true
          schema:
            type: integer
            minimum: 1
      responses:
        '200':
          description: 'Country resource.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CountrySchema'
        '422':
          description: 'Invalid path id (not a positive integer).'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'No country exists for the given id.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '401':
          description: Unauthorized
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: Forbidden
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - meta
  /api/v1/meta/timezones:
    get:
      tags:
        - 'Account Information'
        - 'Public API'
      summary: 'List timezones'
      description: 'Retrieves a paginated list of timezones. Supports pagination, filtering, sorting, and field projection via OData query parameters.'
      operationId: 5c5fb0f1211ae1c9451753f92f1053b6
      parameters:
        -
          name: pageSize
          in: query
          description: 'The number of items to return per page.'
          schema:
            type: integer
            default: 500
        -
          name: page
          in: query
          description: 'The page number to retrieve.'
          schema:
            type: integer
            default: 1
        -
          name: sort
          in: query
          description: 'Ordering by OData (Open Data Protocol) v4 specification'
          schema:
            type: string
            default: ''
        -
          name: select
          in: query
          description: 'Projection (field selection) by OData specification'
          schema:
            type: string
            default: ''
        -
          name: filter
          in: query
          description: 'Filter by an OData (Open Data Protocol) v4 specification'
          schema:
            type: string
            default: ''
      responses:
        '200':
          description: 'A paginated list of timezones'
          content:
            application/json:
              schema:
                title: TimezoneListResponse
                allOf:
                  - { $ref: '#/components/schemas/PagedResponse' }
                  - { properties: { data: { description: 'Array of timezone resources for the current page', type: array, items: { $ref: '#/components/schemas/TimezoneResource' } } }, type: object }
        '400':
          description: 'Invalid query parameters'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '401':
          description: Unauthorized
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: Forbidden
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security: []
  '/api/v1/meta/timezones/{id}':
    get:
      tags:
        - 'Account Information'
        - 'Public API'
      summary: 'Get timezone by ID'
      description: 'Retrieves a single timezone by its numeric ID. Returns the same timezone resource shape used by the list endpoint.'
      operationId: baa7162824294d030115568d1d8e6ca7
      parameters:
        -
          name: id
          in: path
          description: 'The numeric ID of the timezone to retrieve.'
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'The timezone resource'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimezoneResource'
        '401':
          description: Unauthorized
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: Forbidden
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Timezone not found'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - meta
  '/api/v1/meta/timezones/by-zip/{zip}':
    get:
      tags:
        - 'Account Information'
        - 'Public API'
      summary: 'Get timezone by ZIP code'
      description: 'Retrieves the timezone for a US ZIP code. Returns the same timezone resource shape used by the list endpoint. Only US ZIP codes are supported; valid 5-digit ZIPs that are not present in our reference data return a 404.'
      operationId: 10d66d8561dd7dac50ff9c21ef63d83b
      parameters:
        -
          name: zip
          in: path
          description: 'A 5-digit US ZIP code.'
          required: true
          schema:
            type: string
            pattern: '^\d{5}$'
            example: '84604'
      responses:
        '200':
          description: 'The timezone resource'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TimezoneResource'
        '401':
          description: Unauthorized
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: Forbidden
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'No timezone found for the provided ZIP code'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Invalid ZIP format'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - meta
  /api/v1/meta/bank-holidays:
    get:
      tags:
        - 'Public API'
      summary: 'List bank holidays'
      description: 'Returns the list of global bank holiday reference dates (US federal and related bank holidays maintained in BambooHR). This is distinct from company-configured holidays. Optionally filter to holidays whose dates fall in a single calendar year.'
      operationId: get-bank-holidays
      parameters:
        -
          name: year
          in: query
          description: 'When set, only holidays whose dates fall in this calendar year are returned. When omitted, all bank holidays are returned.'
          required: false
          schema:
            type: integer
            maximum: 9999
            minimum: 1
      responses:
        '200':
          description: 'Bank holidays retrieved successfully'
          content:
            application/json:
              schema:
                title: BankHolidaysListResponse
                type: array
                items:
                  title: BankHoliday
                  properties: { id: { description: 'Stable bank holiday identifier', type: integer }, name: { description: 'Holiday display name', type: string }, date: { description: 'Holiday date in YYYY-MM-DD form', type: string, format: date } }
                  type: object
        '422':
          description: 'Invalid query parameters'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '401':
          description: Unauthorized
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: Forbidden
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - meta
  /api/v1/meta/currency-conversions:
    get:
      tags:
        - 'Public API'
      summary: 'Get currency conversion rates'
      description: 'Returns a snapshot of currency conversion rates for the specified base currency, keyed by ISO 4217 currency code. Rate map keys match the `code` field from GET /api/v1/meta/currency/types. Timestamps are ISO 8601 UTC with a Z offset.'
      operationId: get-currency-conversions
      parameters:
        -
          name: baseCurrency
          in: query
          description: 'ISO 4217 base currency code (e.g. USD, EUR). Defaults to USD when omitted.'
          required: false
          schema:
            type: string
            default: USD
            maxLength: 3
            minLength: 3
            pattern: '^[A-Za-z]{3}$'
      responses:
        '200':
          description: 'Currency conversion rates retrieved successfully'
          content:
            application/json:
              schema:
                title: CurrencyConversionsResponse
                properties:
                  baseCurrency: { description: 'ISO 4217 base currency code', type: string, example: USD }
                  lastUpdated: { description: 'ISO 8601 UTC timestamp of the last rate update', type: string, format: date-time, example: '2026-04-07T12:00:00Z' }
                  nextUpdate: { description: 'ISO 8601 UTC timestamp of the next scheduled rate update', type: string, format: date-time, example: '2026-04-08T12:00:00Z' }
                  rates: { description: 'Map of ISO 4217 currency codes to numeric conversion rates relative to the base currency', type: object, additionalProperties: { type: number } }
                type: object
        '400':
          description: 'Invalid parameter type for baseCurrency'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Malformed baseCurrency query parameter'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'No conversion rate snapshot found for the requested base currency'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '401':
          description: Unauthorized
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: Forbidden
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '503':
          description: 'Backing store or dependency temporarily unavailable'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - meta
  /api/v1/hris/org/locations:
    get:
      tags:
        - Locations
        - 'Public API'
      summary: 'List job locations'
      description: 'Retrieves a paginated list of job locations. Returns active locations by default. Supports pagination via page and pageSize query parameters.'
      operationId: get-locations
      parameters:
        -
          name: pageSize
          in: query
          description: 'The number of items to return per page.'
          schema:
            type: integer
            default: 500
        -
          name: page
          in: query
          description: 'The page number to retrieve.'
          schema:
            type: integer
            default: 0
        -
          name: sort
          in: query
          description: 'Ordering by OData (Open Data Protocol) v4 specification. Supported fields: label, archived, manageable, address/address1, address/address2, address/city, address/stateId, address/zipcode, address/countryId, address/timezone, address/remoteLocation, createdAt, archivedAt'
          schema:
            type: string
            default: ''
        -
          name: select
          in: query
          description: 'Projection (field selection) by OData specification. Supported fields: id, label, archived, manageable, address/address1, address/address2, address/city, address/stateId, address/zipcode, address/countryId, address/timezone, address/remoteLocation, createdAt, archivedAt'
          schema:
            type: string
            default: ''
        -
          name: filter
          in: query
          description: 'Filter by an OData (Open Data Protocol) v4 specification. Supported fields: id, label, archived, manageable, address/address1, address/address2, address/city, address/stateId, address/zipcode, address/countryId, address/timezone, address/remoteLocation'
          schema:
            type: string
            default: ''
        -
          name: expand
          in: query
          description: 'Comma-separated list of related resources to expand. Supported values: state, country. Example: expand=state,country'
          explode: false
          schema:
            type: array
            items:
              type: string
              enum:
                - state
                - country
            default: []
      responses:
        '200':
          description: 'A paginated list of job locations'
          content:
            application/json:
              schema:
                title: PagedLocationResponse
                allOf:
                  - { $ref: '#/components/schemas/PagedResponse' }
                  - { properties: { data: { description: 'Array of location resources for the current page', type: array, items: { $ref: '#/components/schemas/LocationResponseObject' } } }, type: object }
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Invalid input.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '429':
          description: 'Too Many Requests'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal Server Error'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - field
    post:
      tags:
        - Locations
        - 'Public API'
      summary: 'Create a job location'
      description: 'Creates a new job location. Requires a label and address. If remoteLocation is true, address fields are optional.'
      operationId: create-location
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - label
                - address
              properties:
                label:
                  description: 'Display name for the location'
                  type: string
                archived:
                  description: 'Whether the location is archived'
                  type: boolean
                  default: false
                address:
                  description: 'Address details for the location. When remoteLocation is true, none are required and must be null or omitted'
                  required: [remoteLocation]
                  properties: { address1: { oneOf: [{ type: string }, { type: 'null' }] }, address2: { oneOf: [{ type: string }, { type: 'null' }] }, city: { oneOf: [{ type: string }, { type: 'null' }] }, stateId: { oneOf: [{ type: string }, { type: 'null' }] }, zipcode: { oneOf: [{ type: string }, { type: 'null' }] }, countryId: { oneOf: [{ type: string }, { type: 'null' }] }, timezone: { description: 'The utcName value from the /api/v1/meta/timezones endpoint', oneOf: [{ type: string }, { type: 'null' }] }, remoteLocation: { type: boolean } }
                  type: object
              type: object
      responses:
        '201':
          description: 'The created location'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LocationResponseObject'
        '403':
          description: 'Permission denied'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Location not found'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '409':
          description: 'Duplicate location label'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Invalid fields'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - field.write
  '/api/v1/hris/org/locations/{id}':
    get:
      tags:
        - Locations
        - 'Public API'
      summary: 'Get a job location'
      description: 'Retrieves a single job location by its ID. Returns the full location resource including address details.'
      operationId: get-location
      parameters:
        -
          name: id
          in: path
          description: 'The ID of the location'
          required: true
          schema:
            type: string
        -
          name: expand
          in: query
          description: 'Comma-separated list of related resources to expand. Supported values: state, country. Example: expand=state,country'
          explode: false
          schema:
            type: array
            items:
              type: string
              enum:
                - state
                - country
            default: []
      responses:
        '200':
          description: 'The requested location'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LocationResponseObject'
        '422':
          description: 'Invalid expand options'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: 'Permission denied'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Location not found'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - field
    put:
      tags:
        - Locations
        - 'Public API'
      summary: 'Update a job location'
      description: 'Updates an existing job location.'
      operationId: update-location
      parameters:
        -
          name: id
          in: path
          description: 'The ID of the location to update'
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              required:
                - label
                - address
              properties:
                label:
                  description: 'Updated display name'
                  type: string
                archived:
                  description: 'Whether the location is archived'
                  type: boolean
                  default: false
                address:
                  description: 'Address details for the location. When remoteLocation is true, none are required and must be null or omitted'
                  required: [remoteLocation]
                  properties: { address1: { oneOf: [{ type: string }, { type: 'null' }] }, address2: { oneOf: [{ type: string }, { type: 'null' }] }, city: { oneOf: [{ type: string }, { type: 'null' }] }, stateId: { oneOf: [{ type: string }, { type: 'null' }] }, zipcode: { oneOf: [{ type: string }, { type: 'null' }] }, countryId: { oneOf: [{ type: string }, { type: 'null' }] }, timezone: { description: 'The utcName value from the /api/v1/meta/timezones endpoint', oneOf: [{ type: string }, { type: 'null' }] }, remoteLocation: { type: boolean } }
                  type: object
              type: object
      responses:
        '200':
          description: 'The updated location'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/LocationResponseObject'
        '403':
          description: 'Permission denied'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Location not found'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '409':
          description: 'Duplicate location label'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '422':
          description: 'Invalid fields'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - field.write
    delete:
      tags:
        - Locations
        - 'Public API'
      summary: 'Delete a job location'
      description: 'Deletes a job location by its ID. Returns 204 No Content on success.'
      operationId: delete-location
      parameters:
        -
          name: id
          in: path
          description: 'The ID of the location to delete'
          required: true
          schema:
            type: string
      responses:
        '204':
          description: 'Location deleted successfully'
        '403':
          description: 'Permission denied'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '409':
          description: 'The location is in use and cannot be deleted. You can archive a location in use to prevent future usage.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '404':
          description: 'Location not found'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Internal server error'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - field.write
  '/api/v1/compensation/total_rewards/{employeeId}':
    get:
      tags:
        - 'Total Rewards'
        - 'Public API'
      summary: 'Check Total Rewards Profile Availability'
      description: 'Check if the given employee has an active Total Rewards profile and the current user has permission to view it. Returns 204 when the employee has a profile and the user is authorized; 404 if the profile does not exist or the user lacks access.'
      operationId: check-total-rewards-profile
      parameters:
        -
          name: employeeId
          in: path
          description: 'The ID of the employee to check.'
          required: true
          schema:
            type: string
      responses:
        '204':
          description: 'Employee has an active Total Rewards profile and the user can view it.'
        '404':
          description: 'Employee does not have a Total Rewards profile or the user lacks permission.'
        '500':
          description: 'Unexpected server error.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - total_rewards
      x-description: 'Check Total Rewards Profile Availability'
  '/api/v1/compensation/total_rewards/{employeeId}/printable':
    get:
      tags:
        - 'Total Rewards'
        - 'Public API'
      summary: 'Get Printable Total Rewards Statement'
      description: "Generate and return a PDF printable version of the employee's Total Rewards statement."
      operationId: get-total-rewards-printable-statement
      parameters:
        -
          name: employeeId
          in: path
          description: 'The ID of the employee.'
          required: true
          schema:
            type: string
        -
          name: multiplier
          in: query
          description: 'Hours-per-week multiplier for annualizing hourly pay.'
          required: false
          schema:
            type: number
        -
          name: year
          in: query
          description: 'Statement year. Defaults to the current year when omitted.'
          required: false
          schema:
            type: integer
      responses:
        '200':
          description: 'PDF document streamed with Content-Disposition: attachment.'
          content:
            application/pdf: {  }
        '400':
          description: 'Invalid argument (e.g. bad year or multiplier).'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '403':
          description: "The authenticated caller lacks permission to view this employee's Total Rewards statement."
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
        '500':
          description: 'Unexpected server error.'
          content:
            application/problem+json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          oauth:
            - total_rewards
      x-description: 'Get Printable Total Rewards Statement'
  /api/v1/meta/fields:
    get:
      tags:
        - 'Account Information'
        - 'Public API'
      summary: 'List Fields'
      description: 'Returns a list of all employee fields available in the account, including field ID, display name, data type, and whether the field is deprecated. Use this endpoint to discover which field names are valid for use with the Get Employee, Datasets, and other field-based endpoints. The response includes standard BambooHR fields as well as any custom fields configured in the account.'
      operationId: list-fields
      parameters:
        -
          $ref: '#/components/parameters/AcceptHeaderParameter'
      responses:
        '200':
          description: 'A list of all available employee fields in the account.'
          content:
            application/json:
              schema:
                title: FieldsResponse
                type: array
                items:
                  title: Field
                  required: [id, name, type]
                  properties: { id: { description: 'The field identifier. For subfields this is a dotted string (e.g., "4340.4"); for top-level fields it may be an integer.', oneOf: [{ type: string }, { type: integer }] }, name: { description: 'The display name of the field.', type: string }, type: { description: 'The data type of the field (e.g., text, date, list, checkbox).', type: string }, alias: { description: 'The API alias for the field. Only present when the field has an alias.', type: string }, deprecated: { description: 'Whether the field is deprecated and should no longer be used. Only present for deprecated fields.', type: boolean } }
                  type: object
            application/xml: {  }
      security:
        -
          basic: []
        -
          oauth:
            - employee
        -
          oauth:
            - field
  /api/v1/meta/users:
    get:
      tags:
        - 'Account Information'
        - 'Public API'
      summary: 'List Users'
      description: "Returns all users for the company, optionally filtered by status. Each user entry includes a user ID, associated employee ID, name, email address (resolved in priority order: work, home, account), account status, and last login time when available.\n\nPass a comma-separated list of status values via the `status` query parameter to filter results. Valid values are `enabled` and `disabled`. If the parameter is omitted or contains only unrecognized values, users of all statuses are returned. Support admin accounts are always excluded from the response.\n\nThe response format is determined by the `Accept` request header. Send `Accept: application/json` to receive JSON; omit the header or send any other value to receive XML."
      operationId: list-users
      parameters:
        -
          $ref: '#/components/parameters/AcceptHeaderParameter'
        -
          name: status
          in: query
          description: 'Comma-separated list of statuses to filter by. Valid values: `enabled`, `disabled`. Defaults to returning users of all statuses when omitted or when no recognized value is provided.'
          required: false
          schema:
            type: string
            example: enabled
      responses:
        '200':
          description: 'An object whose keys are user IDs (as strings) and whose values are user objects. When the Accept header requests XML, a `<users>` document is returned instead.'
          content:
            application/json:
              schema:
                title: ListUsersResponse
                description: 'Object keyed by user ID. Each key is a user ID string; each value is a user object.'
                type: object
                additionalProperties:
                  properties: { id: { description: 'The user ID.', type: integer }, employeeId: { description: 'The ID of the employee record linked to this user. Always present in the JSON response; `0` when no employee record is associated.', type: integer }, firstName: { description: 'First name.', type: string }, lastName: { description: 'Last name.', type: string }, email: { description: "The user's email address. Resolved in priority order: work email, home email, account email.", type: string }, status: { description: 'Account status.', type: string, enum: [enabled, disabled] }, lastLogin: { description: 'ISO 8601 timestamp of the most recent login. Omitted when the user has never logged in.', type: string, format: date-time } }
                  type: object
            application/xml:
              schema:
                title: ListUsersXmlResponse
                properties:
                  user: { type: array, items: { properties: { id: { description: 'The user ID.', type: integer, xml: { attribute: true } }, employeeId: { description: 'The employee record ID. Present as an XML attribute only when the user has an associated employee record.', type: integer, xml: { attribute: true } }, firstName: { description: 'First name.', type: string }, lastName: { description: 'Last name.', type: string }, email: { description: "The user's email address.", type: string }, status: { description: 'Account status.', type: string, enum: [enabled, disabled] }, lastLogin: { description: 'ISO 8601 timestamp of the most recent login. Omitted when the user has never logged in.', type: string, format: date-time } }, type: object, xml: { name: user } }, xml: { name: user, wrapped: false } }
                type: object
                xml:
                  name: users
        '401':
          description: 'Unauthorized. The API key is missing or invalid.'
        '403':
          description: 'Forbidden. The caller does not have permission to access this endpoint.'
      security:
        -
          basic: []
        -
          oauth:
            - user
  '/api/v1/employees/changed/tables/{table}':
    get:
      tags:
        - 'Change Tracking'
        - 'Public API'
      summary: 'Get Changed Employee Table Data'
      description: "Returns table data for employees that have changed since the given timestamp. This is an optimization to avoid downloading all table data for all employees. It operates on an employee-last-changed-timestamp, which means that a change in ANY field in the employee record will cause ALL of that employee's table rows to show up via this API. The response includes the table rows grouped by employee ID with their last-changed timestamps."
      operationId: get-changed-employee-table-data
      parameters:
        -
          name: table
          in: path
          description: 'The API name of the table to retrieve changed data for. See the TableName schema for valid standard values; custom tables use a `custom` prefix. Use GET /api/v1/meta/tables to discover the complete list.'
          required: true
          schema:
            $ref: '#/components/schemas/TableName'
        -
          name: since
          in: query
          description: 'ISO 8601 timestamp (URL-encoded). Only employees changed since this timestamp will be returned.'
          required: true
          schema:
            type: string
            format: date-time
      responses:
        '200':
          description: 'Changed table data for the specified table, grouped by employee ID.'
          content:
            application/json:
              schema:
                title: ChangedEmployeeTableDataResponse
                required:
                  - table
                  - employees
                properties:
                  table: { description: 'The requested table name.', type: string }
                  employees: { description: 'Map of employee IDs to changed table data for that employee.', type: object, additionalProperties: { properties: { lastChanged: { description: 'ISO 8601 timestamp of the last change for this employee.', type: string, format: date-time }, rows: { description: 'The table rows for this employee.', type: array, items: { type: object, additionalProperties: { anyOf: [{ type: string }, { type: number }, { type: boolean }, { type: integer }, { type: array, items: { anyOf: [{ type: string }, { type: number }, { type: boolean }, { type: integer }, { type: object, additionalProperties: true }, { type: 'null' }] } }, { type: object, additionalProperties: true }, { type: 'null' }] } } } }, type: object } }
                type: object
            application/xml: {  }
        '400':
          description: 'The since parameter is missing or is not a valid ISO 8601 date.'
        '404':
          description: 'The specified table does not exist.'
      security:
        -
          basic: []
        -
          oauth:
            - employee
        -
          oauth:
            - 'employee:job'
        -
          oauth:
            - 'employee:compensation'
        -
          oauth:
            - 'employee:custom_fields'
        -
          oauth:
            - 'employee:custom_fields_encrypted'
        -
          oauth:
            - 'employee:assets'
        -
          oauth:
            - 'employee:emergency_contacts'
        -
          oauth:
            - 'sensitive_employee:creditcards'
        -
          oauth:
            - 'employee:education'
  '/api/v1/employees/{id}/tables/{table}':
    get:
      tags:
        - 'Employee Tables'
        - 'Public API'
      summary: 'Get Employee Table Data'
      description: 'Returns all rows for a given employee and table combination. The result is not sorted in any particular order. Each row contains the fields defined for that table, subject to field-level permission checks. Fields the caller does not have access to are omitted from the response.'
      operationId: get-employee-table-data
      parameters:
        -
          name: id
          in: path
          description: 'The employee ID. Use the special value "all" to retrieve table data for all employees the API user has access to.'
          required: true
          schema:
            type: string
        -
          name: table
          in: path
          description: 'The API name of the table to retrieve. See the TableName schema for standard values; custom tables also accepted (e.g., `custom1`, `custom42`). Use the `list-tabular-fields` tool (GET /api/v1/meta/tables) to discover custom table names.'
          required: true
          schema:
            $ref: '#/components/schemas/TableName'
        -
          $ref: '#/components/parameters/AcceptHeaderParameter'
      responses:
        '200':
          description: 'All rows for the specified employee and table.'
          content:
            application/json:
              schema:
                title: EmployeeTableDataResponse
                type: array
                items:
                  title: EmployeeTableRow
                  description: "A single row from the requested table. The returned fields depend on the table and the caller's field-level permissions."
                  properties: { id: { description: 'The table row ID.', type: string }, employeeId: { description: 'The employee ID that owns this row.', type: string } }
                  type: object
                  additionalProperties: { anyOf: [{ type: string }, { type: number }, { type: boolean }, { type: integer }, { type: array, items: { anyOf: [{ type: string }, { type: number }, { type: boolean }, { type: integer }, { type: object, additionalProperties: true }, { type: 'null' }] } }, { type: object, additionalProperties: true }, { type: 'null' }] }
            application/xml: {  }
        '404':
          description: 'The employee or table does not exist.'
      security:
        -
          basic: []
        -
          oauth:
            - employee
        -
          oauth:
            - 'employee:job'
        -
          oauth:
            - 'employee:compensation'
        -
          oauth:
            - 'employee:custom_fields'
        -
          oauth:
            - 'employee:custom_fields_encrypted'
        -
          oauth:
            - 'employee:assets'
        -
          oauth:
            - 'employee:emergency_contacts'
        -
          oauth:
            - 'sensitive_employee:creditcards'
        -
          oauth:
            - 'employee:education'
        -
          oauth:
            - 'employee:job.write'
    post:
      tags:
        - 'Employee Tables'
        - 'Public API'
      summary: 'Create Table Row'
      description: 'Add a new row to the specified employee table by submitting field name/value pairs in JSON or XML. Use this endpoint to append records to tabular employee data such as job information or compensation history.'
      operationId: create-table-row
      parameters:
        -
          name: id
          in: path
          description: 'The employee ID.'
          required: true
          schema:
            type: string
        -
          name: table
          in: path
          description: 'The API name of the table to add a row to. See the TableName schema for valid standard values; custom tables use a `custom` prefix. Use GET /api/v1/meta/tables to discover the complete list.'
          required: true
          schema:
            $ref: '#/components/schemas/TableName'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TableRowUpdate'
          application/xml:
            schema:
              $ref: '#/components/schemas/TableRowUpdate'
          text/xml:
            schema:
              $ref: '#/components/schemas/TableRowUpdate'
      responses:
        '200':
          description: 'Row added successfully. No response body is returned.'
        '400':
          description: 'The posted JSON or XML is malformed, or required fields are missing.'
        '403':
          description: 'Permission denied.'
        '404':
          description: 'The employee or table does not exist.'
        '406':
          description: 'One or more field values are invalid.'
        '409':
          description: 'Conflict. The provided field values conflict with business rules or payroll constraints.'
        '412':
          description: 'Precondition failed. The update requires an active pay schedule or other required prerequisite data.'
      security:
        -
          basic: []
        -
          oauth:
            - employee.write
        -
          oauth:
            - 'employee:job.write'
        -
          oauth:
            - 'employee:compensation.write'
        -
          oauth:
            - 'employee:custom_fields.write'
        -
          oauth:
            - 'employee:custom_fields_encrypted.write'
        -
          oauth:
            - 'employee:assets.write'
        -
          oauth:
            - 'employee:emergency_contacts.write'
        -
          oauth:
            - 'sensitive_employee:creditcards.write'
        -
          oauth:
            - 'employee:education.write'
        -
          oauth:
            - employee
        -
          oauth:
            - 'employee:job'
  '/api/v1_1/employees/{id}/tables/{table}/{rowId}':
    post:
      tags:
        - 'Employee Tables'
        - 'Public API'
      summary: 'Update Table Row v1.1'
      description: 'Update an existing row in the specified employee table using the v1.1 table-row update endpoint. Submit the field changes in JSON or XML. This version is largely compatible with v1 and is intended for the same tabular employee data use cases.'
      operationId: update-table-row-v1-1
      parameters:
        -
          name: id
          in: path
          description: 'The employee ID.'
          required: true
          schema:
            type: string
        -
          name: table
          in: path
          description: 'The API name of the table containing the row to update. See the TableName schema for valid standard values; custom tables use a `custom` prefix. Use GET /api/v1/meta/tables to discover the complete list.'
          required: true
          schema:
            $ref: '#/components/schemas/TableName'
        -
          name: rowId
          in: path
          description: 'The ID of the row to update.'
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TableRowUpdate'
          application/xml:
            schema:
              $ref: '#/components/schemas/TableRowUpdate'
          text/xml:
            schema:
              $ref: '#/components/schemas/TableRowUpdate'
      responses:
        '200':
          description: 'Row updated successfully. A 200 response is returned as long as at least one field was updated, even if some fields were skipped due to permissions. No response body is returned.'
        '400':
          description: 'The posted JSON or XML is malformed, or required fields are missing.'
        '403':
          description: 'Permission denied.'
        '404':
          description: 'The employee, table, or row does not exist.'
        '406':
          description: 'One or more field values are invalid.'
        '409':
          description: 'Conflict. The provided field values conflict with business rules or payroll constraints.'
        '412':
          description: 'Precondition failed. The update requires an active pay schedule or other required prerequisite data.'
      security:
        -
          basic: []
        -
          oauth:
            - employee.write
        -
          oauth:
            - 'employee:job.write'
        -
          oauth:
            - 'employee:compensation.write'
        -
          oauth:
            - 'employee:custom_fields.write'
        -
          oauth:
            - 'employee:custom_fields_encrypted.write'
        -
          oauth:
            - 'employee:assets.write'
        -
          oauth:
            - 'employee:emergency_contacts.write'
        -
          oauth:
            - 'sensitive_employee:creditcards.write'
        -
          oauth:
            - 'employee:education.write'
  '/api/v1_1/employees/{id}/tables/{table}':
    post:
      tags:
        - 'Employee Tables'
        - 'Public API'
      summary: 'Create Table Row v1.1'
      description: 'Add a new row to the specified employee table using the v1.1 table-row creation endpoint. Submit the new row in JSON or XML. This version is largely compatible with v1 and supports the same tabular employee data use cases.'
      operationId: create-table-row-v1-1
      parameters:
        -
          name: id
          in: path
          description: 'The employee ID.'
          required: true
          schema:
            type: string
        -
          name: table
          in: path
          description: 'The API name of the table to add a row to. See the TableName schema for valid standard values; custom tables use a `custom` prefix. Use GET /api/v1/meta/tables to discover the complete list.'
          required: true
          schema:
            $ref: '#/components/schemas/TableName'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TableRowUpdate'
          application/xml:
            schema:
              $ref: '#/components/schemas/TableRowUpdate'
          text/xml:
            schema:
              $ref: '#/components/schemas/TableRowUpdate'
      responses:
        '200':
          description: 'Row added successfully. No response body is returned.'
        '400':
          description: 'The posted JSON or XML is malformed, or required fields are missing.'
        '403':
          description: 'Permission denied.'
        '404':
          description: 'The employee or table does not exist.'
        '406':
          description: 'One or more field values are invalid.'
        '409':
          description: 'Conflict. The provided field values conflict with business rules or payroll constraints.'
        '412':
          description: 'Precondition failed. The update requires an active pay schedule or other required prerequisite data.'
      security:
        -
          basic: []
        -
          oauth:
            - employee.write
        -
          oauth:
            - 'employee:job.write'
        -
          oauth:
            - 'employee:compensation.write'
        -
          oauth:
            - 'employee:custom_fields.write'
        -
          oauth:
            - 'employee:custom_fields_encrypted.write'
        -
          oauth:
            - 'employee:assets.write'
        -
          oauth:
            - 'employee:emergency_contacts.write'
        -
          oauth:
            - 'sensitive_employee:creditcards.write'
        -
          oauth:
            - 'employee:education.write'
  /api/v1/employees/changed:
    get:
      tags:
        - 'Change Tracking'
        - 'Public API'
      summary: 'Get Changed Employee IDs'
      description: 'Returns a list of employee IDs that have changed since the given timestamp. This allows for efficient syncing of employee data — rather than downloading all employees, only those that have changed are returned. A change in ANY individual field in the employee record, as well as any change to the employment status, job info, or compensation tables, will cause that employee to be returned. Each entry includes the employee ID, the type of change (Inserted, Updated, or Deleted), and the last-changed timestamp.'
      operationId: get-changed-employee-ids
      parameters:
        -
          name: since
          in: query
          description: 'ISO 8601 timestamp (URL-encoded). Only employees changed since this timestamp will be returned.'
          required: true
          schema:
            type: string
            format: date-time
        -
          name: type
          in: query
          description: 'Filter by change type. If omitted, all change types are returned.'
          required: false
          schema:
            type: string
            enum:
              - inserted
              - updated
              - deleted
              - all
      responses:
        '200':
          description: 'The latest change timestamp in the result set and a map of employees changed since the given timestamp.'
          content:
            application/json:
              schema:
                title: ChangedEmployeeIdsResponse
                required:
                  - latest
                  - employees
                properties:
                  latest: { description: 'The latest last-changed timestamp among the returned employees.', type: string, format: date-time }
                  employees: { description: 'Map of employee IDs to change metadata for each changed employee.', type: object, additionalProperties: { properties: { id: { description: 'The employee ID.', type: string }, action: { description: 'The type of change: Inserted, Updated, or Deleted.', type: string, enum: [Inserted, Updated, Deleted] }, lastChanged: { description: 'ISO 8601 timestamp of the last change.', type: string, format: date-time } }, type: object } }
                type: object
            application/xml: {  }
        '400':
          description: 'The since parameter is missing, is not a valid ISO 8601 date, or the type parameter is invalid.'
      security:
        -
          basic: []
        -
          oauth:
            - public.user
        -
          oauth:
            - public.integration
  '/api/v1/employees/{employeeId}/photo/{size}':
    get:
      tags:
        - Photos
        - 'Public API'
      summary: 'Get Employee Photo'
      description: "Returns an employee photo at the requested size. Available sizes are: `original` (full resolution), `large` (340×340), `medium` (170×170), `small` (150×150), `xs` (50×50), and `tiny` (20×20). The response shape is selected via standard HTTP content negotiation: by default the body is the stored image bytes and the `Content-Type` response header matches the body (`image/jpeg`, `image/png`, `image/bmp`, `image/gif`, or `image/tiff`). When the caller sends `Accept: application/json` (typical MCP/LLM-connector usage), the body is a JSON object `{ mimeType, fileBase64 }` with the same image bytes base64-encoded, and the response `Content-Type` is `application/json`.\n\nA 404 response covers three distinct cases: (1) the employee exists but has no photo on file (a normal, non-error state), (2) the employee ID does not exist, or (3) the size value is not one of the recognized options. The `x-bamboohr-error-message` response header distinguishes them: `Employee photo not found`, `Employee not found`, or `Size: \"<value>\" is not a valid size option. Valid sizes are: xs, small, tiny, original, medium and large.`. Treat \"Employee photo not found\" as a normal \"no photo on file\" result rather than as a bad employee ID, permission failure, or other error to debug."
      operationId: get-employee-photo
      parameters:
        -
          name: employeeId
          in: path
          description: 'The ID of the employee whose photo to retrieve.'
          required: true
          schema:
            type: integer
        -
          name: size
          in: path
          description: 'The desired photo size. One of: `original`, `large`, `medium`, `small`, `xs`, `tiny`.'
          required: true
          schema:
            type: string
            enum:
              - original
              - large
              - medium
              - small
              - xs
              - tiny
        -
          name: width
          in: query
          description: 'Optional. Scales the returned image to the specified pixel width, capped at the natural width of the requested size. Only applies to `small` and `tiny` sizes.'
          required: false
          schema:
            type: integer
        -
          name: height
          in: query
          description: 'Optional. Scales the returned image to the specified pixel height, capped at the natural height of the requested size. Only applies to `small` and `tiny` sizes.'
          required: false
          schema:
            type: integer
      responses:
        '200':
          description: 'Employee photo, returned in one of two shapes via standard HTTP content negotiation: binary `image/*` bytes by default (matching the stored format), or `application/json` with `{ mimeType, fileBase64 }` when the caller sends `Accept: application/json`.'
          content:
            'image/*': {  }
            application/json:
              schema:
                title: EmployeePhotoJsonResponse
                required:
                  - mimeType
                  - fileBase64
                properties:
                  mimeType: { description: 'The actual MIME type of the underlying image (e.g. `image/jpeg`, `image/png`, `image/bmp`, `image/gif`, `image/tiff`). Use this to identify the format of the decoded bytes.', type: string }
                  fileBase64: { description: 'Base64-encoded image bytes. Decoding produces the same bytes returned by the binary variant of this endpoint.', type: string, format: byte }
                type: object
        '403':
          description: "The authenticated user does not have permission to view this employee's photo."
        '404':
          description: 'Returned in three distinct cases: the employee does not exist, the employee has no photo on file, or the requested size is not a recognized value. The `x-bamboohr-error-message` response header reports which case applies (`Employee not found`, `Employee photo not found`, or `Size: "<value>" is not a valid size option. Valid sizes are: xs, small, tiny, original, medium and large.`). "Employee photo not found" is the expected response for an employee who simply has not uploaded a photo and should not be treated as an error to debug.'
          headers:
            x-bamboohr-error-message:
              description: 'Identifies which 404 case occurred. One of: `Employee not found`, `Employee photo not found`, or `Size: "<value>" is not a valid size option. Valid sizes are: xs, small, tiny, original, medium and large.`.'
              schema:
                type: string
      security:
        -
          basic: []
        -
          oauth:
            - 'employee:photo'
  /api/v1/reports/custom:
    post:
      tags:
        - Reports
        - 'Public API'
      summary: 'Request Custom Report'
      description: "**Deprecated. Use Datasets > Get Data from Dataset instead.**\n\nGenerates an ad-hoc employee report based on a caller-specified list of fields and optional filters. Returns report data in the requested format (JSON, XML, CSV, XLS, or PDF). The report includes all employees regardless of status (both Active and Inactive), unlike the BambooHR UI which filters to Active employees by default.\n\nThe request body may be submitted as JSON or XML. To submit JSON, set `Content-Type: application/json` exactly — any variation such as `application/json; charset=UTF-8` is not recognised as JSON and the body will be parsed as XML instead, which typically results in `400 Malformed XML`. To submit XML, set `Content-Type` to any other value; the body must be a `<report>` document as described in the XML request body schema.\n\nThe `format` query parameter is case-insensitive (`json`, `JSON`, `Json` are all accepted). If `format` is omitted, the output format is inferred from the `Accept` header, but only these exact values are supported: `application/json`, `text/xml`, `text/csv`, `application/pdf`, `application/vnd.ms-excel`. Any other `Accept` value (including `application/xml` and `*/*`) will return 404.\n\nField IDs in the request that are unknown or that the caller does not have permission to view are silently omitted from the report — the endpoint still returns 200. The `filters` object supports `lastChanged` (ISO 8601 date-time to filter employees by last-modified date, with optional `includeNull` control) and `employeeIds` (restrict results to specific employee IDs). The maximum number of fields per request is 400."
      operationId: request-custom-report
      parameters:
        -
          name: Accept
          in: header
          description: 'The desired response content type when `format` is omitted. Accepted values: `application/json`, `text/xml`, `text/csv`, `application/pdf`, `application/vnd.ms-excel`. Any other value returns 404.'
          schema:
            type: string
            enum:
              - application/json
              - text/xml
              - text/csv
              - application/pdf
              - application/vnd.ms-excel
        -
          name: format
          in: query
          description: 'The output format for the report. Case-insensitive. If omitted, format is inferred from the `Accept` header — only `application/json`, `text/xml`, `text/csv`, `application/pdf`, and `application/vnd.ms-excel` are accepted; any other value returns 404.'
          required: false
          schema:
            type: string
            enum:
              - json
              - xml
              - csv
              - xls
              - pdf
        -
          name: onlyCurrent
          in: query
          description: 'Whether to restrict historical fields to current values only. Set to false to include future-dated history values in the report output. Defaults to true.'
          schema:
            type: boolean
            default: true
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RequestCustomReport'
          application/xml:
            schema:
              title: RequestCustomReportXml
              description: "XML report definition. The root element must be `<report>`. Supported child elements:\n\n- `<title>` — optional report label string.\n- `<fields>` — required; contains one `<field id=\"fieldAlias\"/>` element per column, where `id` is the BambooHR field alias (e.g. `firstName`, `workEmail`). Unknown or inaccessible field IDs are silently omitted.\n- `<filterDuplicates>` — optional; set to `no` to disable duplicate row filtering (default is enabled).\n- `<filters>` — optional; supported child elements:\n  - `<lastChanged includeNull=\"yes|no\">2025-01-01T00:00:00+0000</lastChanged>` — ISO 8601 date-time string (offset format `+0000` or `+00:00`). `includeNull` defaults to `yes`.\n  - `<employeeIds>` — contains one `<employeeId>` element per integer employee ID to include."
              type: object
      responses:
        '200':
          description: 'Report data in the requested format. For JSON, returns an object with a `title` string, a `fields` array (each with `id`, `type`, and `name`), and an `employees` array (each with `id` and one key per requested field). For XML, returns a `<report>` document. For CSV/XLS/PDF, returns the file content with the appropriate content-type header.'
          content:
            application/json:
              schema:
                title: RequestCustomReportResponse
                properties:
                  title: { description: 'The report title.', type: string }
                  fields: { description: 'Metadata for each field included in the report.', type: array, items: { properties: { id: { description: 'The field ID.', type: string }, type: { description: 'The field data type.', type: string }, name: { description: 'The human-readable field label.', type: string } }, type: object } }
                  employees: { description: 'One object per employee. Each object contains an `id` property plus one key per requested field.', type: array, items: { properties: { id: { description: 'The employee ID.', type: string } }, type: object, additionalProperties: { type: string } } }
                type: object
            text/xml: {  }
            text/csv: {  }
            application/pdf: {  }
            application/vnd.ms-excel: {  }
        '400':
          description: 'Bad request. Returned when the request body is malformed JSON or XML, or when more than 400 fields are requested. Unknown or inaccessible field IDs are silently omitted and do not cause a 400.'
        '404':
          description: 'Report format not found. Returned when an unsupported `format` value is supplied (e.g. `?format=bogus`), or when `format` is omitted and the `Accept` header is not one of the supported exact values (`application/json`, `text/xml`, `text/csv`, `application/pdf`, `application/vnd.ms-excel`).'
      deprecated: true
      security:
        -
          basic: []
        -
          oauth:
            - report
  /api/v1/employees/files/categories:
    post:
      tags:
        - 'Employee Files'
        - 'Public API'
      summary: 'Create Employee File Category'
      description: 'Creates one or more employee file categories (not company file categories). The request body is a JSON array of category name strings or an equivalent XML document with `<category>` elements. Each name must be non-empty and unique among existing employee file categories. An empty array returns 200 without creating anything. On success, returns 201 with no body. The admin user group is automatically granted edit permission on each new category.'
      operationId: create-employee-file-category
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PostNewEmployeeFileCategory'
          application/xml:
            schema:
              $ref: '#/components/schemas/PostNewEmployeeFileCategory'
      responses:
        '200':
          description: 'The request was processed but no categories were created (empty payload).'
        '201':
          description: 'All specified categories were created successfully. No response body is returned.'
        '400':
          description: 'The request body contains malformed JSON or XML, an empty category name, or a category name that already exists.'
        '403':
          description: 'The API user does not have permission to create employee file categories.'
        '500':
          description: 'An internal server error occurred.'
      security:
        -
          basic: []
        -
          oauth:
            - 'employee:file.write'
  /api/v1/files/categories:
    post:
      tags:
        - 'Company Files'
        - 'Public API'
      summary: 'Create Company File Category'
      description: 'Creates one or more company file categories. Accepts a JSON array of category name strings or an equivalent XML document. An empty payload returns 200 without creating anything. Returns 400 if a name is empty or already exists, 403 if the caller lacks permission or the name is reserved, and 500 on an internal error.'
      operationId: create-company-file-category
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NewCompanyFileCategory'
          application/xml:
            schema:
              $ref: '#/components/schemas/NewCompanyFileCategory'
      responses:
        '200':
          description: 'The request was processed but no categories were created (empty payload).'
        '201':
          description: 'All specified categories were created successfully.'
        '400':
          description: 'The request body contains malformed JSON or XML, an empty category name, or a category name that already exists.'
        '403':
          description: 'The API user does not have permission to create company file categories, or the category name is reserved.'
        '500':
          description: 'An internal server error occurred.'
      security:
        -
          basic: []
        -
          oauth:
            - company_file.write
  '/api/v1/employees/{id}/files':
    post:
      tags:
        - 'Employee Files'
        - 'Public API'
      summary: 'Upload Employee File'
      description: "Uploads a file to an employee's file section. The request must be a `multipart/form-data` POST. On success, a `Location` header is returned with the URL of the newly created file resource. The file must be under 20MB and use a supported extension. Pass `0` as the employee ID to use the employee associated with the API key. Employees may upload to their own folder if the company has employee document uploads enabled."
      operationId: upload-employee-file
      parameters:
        -
          name: id
          in: path
          description: '{id} is an employee ID. The special employee ID of zero (0) means to use the employee ID associated with the API key (if any).'
          required: true
          schema:
            type: string
            default: '0'
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              required:
                - fileName
                - category
                - file
              properties:
                fileName:
                  description: 'The display name for the uploaded file.'
                  type: string
                category:
                  description: 'The ID of the employee file section to upload the file into.'
                  type: integer
                share:
                  description: 'Whether to share the file with the employee. Accepted values: `yes` or `no`. Defaults to `no`.'
                  type: string
                  enum: ['yes', 'no']
                file:
                  description: 'The file to upload.'
                  type: string
                  format: binary
              type: object
      responses:
        '201':
          description: 'The file was uploaded successfully. The `Location` header contains the URL of the new file resource.'
        '400':
          description: 'The request is invalid: missing file name, missing file, zero-byte file, or unsupported file extension.'
        '403':
          description: 'The API user does not have permission to access the requested employee or their file section.'
        '404':
          description: 'The employee or the specified file category was not found.'
        '413':
          description: "The file exceeds the 20MB limit or the company's storage limit."
        '503':
          description: 'The file storage API is temporarily unavailable.'
      security:
        -
          basic: []
        -
          oauth:
            - 'employee:file.write'
  /api/v1/files:
    post:
      tags:
        - 'Company Files'
        - 'Public API'
      summary: 'Upload Company File'
      description: 'Uploads a file to a company file category. The request must be a `multipart/form-data` POST. On success, a `Location` header is returned with the URL of the newly created file resource. The file must be under 20MB and use a supported extension. Uploading to read-only categories is not permitted. Uploading to implementation categories is not permitted on companies that have completed implementation.'
      operationId: upload-company-file
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              required:
                - fileName
                - category
                - file
              properties:
                fileName:
                  description: 'The display name for the uploaded file.'
                  type: string
                category:
                  description: 'The ID of the file category (section) to upload the file into.'
                  type: integer
                share:
                  description: 'Whether to share the file with all employees. Accepted values: `yes` or `no`. Defaults to `no`.'
                  type: string
                  enum: ['yes', 'no']
                file:
                  description: 'The file to upload.'
                  type: string
                  format: binary
              type: object
      responses:
        '201':
          description: 'The file was uploaded successfully. The `Location` header contains the URL of the new file resource.'
        '400':
          description: 'The request is invalid: missing file name, missing file, zero-byte file, or unsupported file extension.'
        '403':
          description: 'The API user does not have permission to upload files, or the target category is read-only.'
        '404':
          description: 'The specified category ID was not found.'
        '413':
          description: "The file exceeds the 20MB limit or the company's storage limit."
        '503':
          description: 'The file storage API is temporarily unavailable.'
      security:
        -
          basic: []
        -
          oauth:
            - company_file.write
  '/api/v1/employees/{employeeId}/time_off/policies':
    get:
      tags:
        - 'Time Off'
        - 'Public API'
      summary: 'List Employee Time Off Policies'
      description: 'Returns the time off policies currently assigned to the specified employee, including policy ID, time off type, and accrual start date.'
      operationId: list-employee-time-off-policies
      parameters:
        -
          name: employeeId
          in: path
          description: 'The ID of the employee.'
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'The list of time off policies assigned to the employee. Only includes regular (accruing) policy types.'
          content:
            application/json:
              schema:
                title: EmployeeTimeOffPoliciesResponse
                type: array
                items:
                  title: EmployeeTimeOffPolicyAssignment
                  properties: { timeOffPolicyId: { description: 'The ID of the assigned time off policy.', type: integer }, timeOffTypeId: { description: 'The ID of the time off type.', type: integer }, accrualStartDate: { description: 'The date accruals started in YYYY-MM-DD format.', type: string, format: date } }
                  type: object
        '401':
          description: 'Unauthorized. Invalid API credentials.'
        '403':
          description: 'Insufficient permissions to view time off policy assignments.'
      security:
        -
          basic: []
        -
          oauth:
            - time_off
    put:
      tags:
        - 'Time Off'
        - 'Public API'
      summary: 'Assign Time Off Policies'
      description: 'Assigns time off policies to an employee with accruals starting on the specified date. A null start date removes the existing assignment. On success, returns the current list of assigned policies.'
      operationId: assign-time-off-policies
      parameters:
        -
          name: employeeId
          in: path
          description: 'The ID of the employee to assign policies to.'
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: array
              items:
                required:
                  - timeOffPolicyId
                  - accrualStartDate
                properties:
                  timeOffPolicyId: { description: 'The ID of the time off policy to assign.', type: integer }
                  accrualStartDate: { description: 'The date accruals should start in YYYY-MM-DD format. Set to null to remove an existing assignment.', oneOf: [{ type: string, format: date }, { type: 'null' }] }
                type: object
              example:
                -
                  timeOffPolicyId: 4
                  accrualStartDate: '2024-02-01'
                -
                  timeOffPolicyId: 5
                  accrualStartDate: null
      responses:
        '200':
          description: 'The current list of assigned policies for the employee.'
          content:
            application/json:
              schema:
                title: AssignedTimeOffPoliciesResponse
                type: array
                items:
                  title: AssignedTimeOffPolicy
                  properties: { timeOffPolicyId: { description: 'The ID of the assigned time off policy.', type: integer }, timeOffTypeId: { description: 'The ID of the time off type.', type: integer }, accrualStartDate: { description: 'The date accruals started in YYYY-MM-DD format.', type: string, format: date } }
                  type: object
        '400':
          description: 'Invalid request. Possible causes: employee not found, missing hire date, malformed JSON, missing timeOffPolicyId, or a policy is already assigned for the time off type.'
        '401':
          description: 'Unauthorized. Invalid API credentials.'
        '403':
          description: 'Insufficient permissions to assign time off policies.'
        '422':
          description: 'Unprocessable entity. The operation is not allowed for this employee, for example because of EOR restrictions.'
      security:
        -
          basic: []
        -
          oauth:
            - time_off.write
  '/api/v1_1/employees/{employeeId}/time_off/policies':
    get:
      tags:
        - 'Time Off'
        - 'Public API'
      summary: 'List Employee Time Off Policies v1.1'
      description: 'Returns the time off policies currently assigned to a specific employee, as a list of `{timeOffPolicyId, timeOffTypeId, accrualStartDate}` records. Use this to find which policy governs each time off type for this employee and when their accruals began. This is the per-employee assignment view; use `list-time-off-policies` for the company-wide policy catalog. Includes all policy types (accruing, manual, and unlimited); the v1 form of this endpoint excluded manual and unlimited types — v1.1 includes them.'
      operationId: list-employee-time-off-policies-v1.1
      parameters:
        -
          name: employeeId
          in: path
          description: 'The ID of the employee.'
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'The list of time off policies assigned to the employee, including manual and unlimited types.'
          content:
            application/json:
              schema:
                title: EmployeeTimeOffPoliciesV1_1Response
                type: array
                items:
                  title: EmployeeTimeOffPolicyAssignmentV1_1
                  properties: { timeOffPolicyId: { description: 'The ID of the assigned time off policy.', type: integer }, timeOffTypeId: { description: 'The ID of the time off type.', type: integer }, accrualStartDate: { description: 'The date accruals started in YYYY-MM-DD format. Null for manual and unlimited policy types.', oneOf: [{ type: string, format: date }, { type: 'null' }] } }
                  type: object
        '401':
          description: 'Unauthorized. Invalid API credentials.'
        '403':
          description: 'Insufficient permissions to view time off policy assignments.'
      security:
        -
          basic: []
        -
          oauth:
            - time_off
    put:
      tags:
        - 'Time Off'
        - 'Public API'
      summary: 'Assign Time Off Policies v1.1'
      description: 'Assigns time off policies to an employee with accruals starting on the specified date. On success, returns the current list of assigned policies including manual and unlimited policy types.'
      operationId: assign-time-off-policies-v1.1
      parameters:
        -
          name: employeeId
          in: path
          description: 'The ID of the employee to assign policies to.'
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: array
              items:
                required:
                  - timeOffPolicyId
                  - accrualStartDate
                properties:
                  timeOffPolicyId: { description: 'The ID of the time off policy to assign.', type: integer }
                  accrualStartDate: { description: 'The date accruals should start in YYYY-MM-DD format. Set to null to remove an existing assignment.', oneOf: [{ type: string, format: date }, { type: 'null' }] }
                type: object
              example:
                -
                  timeOffPolicyId: 4
                  accrualStartDate: '2024-02-01'
                -
                  timeOffPolicyId: 5
                  accrualStartDate: null
      responses:
        '200':
          description: 'The current list of assigned policies for the employee, including manual and unlimited types.'
          content:
            application/json:
              schema:
                title: AssignedTimeOffPoliciesV1_1Response
                type: array
                items:
                  title: AssignedTimeOffPolicyV1_1
                  properties: { timeOffPolicyId: { description: 'The ID of the assigned time off policy.', type: integer }, timeOffTypeId: { description: 'The ID of the time off type.', type: integer }, accrualStartDate: { description: 'The date accruals started in YYYY-MM-DD format. Null for manual and unlimited policy types.', oneOf: [{ type: string, format: date }, { type: 'null' }] } }
                  type: object
        '400':
          description: 'Invalid request. Possible causes: malformed JSON, missing required fields, missing hire date, or duplicate policy type.'
        '401':
          description: 'Unauthorized. Invalid API credentials.'
        '403':
          description: 'Insufficient permissions to assign time off policies.'
        '422':
          description: 'Unprocessable entity. The operation is not allowed for this employee (e.g., EOR restrictions).'
        '500':
          description: 'Internal server error.'
      security:
        -
          basic: []
        -
          oauth:
            - time_off.write
  '/api/v1/applicant_tracking/applications/{applicationId}':
    get:
      tags:
        - 'Public API'
        - 'Applicant Tracking'
      summary: 'Get Job Application Details'
      description: 'Get the full details of a single application including applicant info, job details, questions and answers, and status history. The owner of the API key used must have access to ATS settings.'
      operationId: get-application-details
      parameters:
        -
          name: applicationId
          in: path
          description: 'The ID of the application to retrieve details for.'
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Success. Returns the full application detail object.'
          content:
            application/json:
              schema:
                title: ApplicationDetails
                properties:
                  id: { description: 'Application ID', type: integer }
                  appliedDate: { description: 'ISO 8601 datetime when the application was submitted', type: string, format: date-time }
                  status: { properties: { id: { description: 'Status ID', type: integer }, label: { description: 'Status name', type: string }, dateChanged: { description: 'ISO 8601 datetime when status was last changed', type: string, format: date-time }, changedByUser: { description: 'The user who last changed the status, or null if changed by the system', properties: { id: { description: 'User ID', type: integer }, firstName: { description: 'First name', type: string }, lastName: { description: 'Last name', type: string }, avatar: { description: 'URL to avatar image', oneOf: [{ type: string }, { type: 'null' }] }, jobTitle: { description: "User's job title", properties: { id: { oneOf: [{ type: integer }, { type: 'null' }] }, label: { oneOf: [{ type: string }, { type: 'null' }] } }, oneOf: [{ type: object }, { type: 'null' }] } }, oneOf: [{ type: object }, { type: 'null' }] } }, type: object }
                  rating: { description: 'Applicant rating (1-5), or null if not yet rated', oneOf: [{ type: integer }, { type: 'null' }] }
                  resumeFileId: { description: 'ID of the attached resume file, or null if not provided', oneOf: [{ type: integer }, { type: 'null' }] }
                  coverLetterFileId: { description: 'ID of the attached cover letter file, or null if not provided', oneOf: [{ type: integer }, { type: 'null' }] }
                  attachmentCount: { description: 'Total number of attachments on this application, or null', oneOf: [{ type: integer }, { type: 'null' }] }
                  attachments: { description: 'List of attachments on this application, or null if none exist', oneOf: [{ type: array, items: { properties: { id: { description: 'Attachment ID', type: integer }, name: { description: 'Attachment file name', type: string }, fileUrl: { description: 'URL to download the attachment', type: string } }, type: object } }, { type: 'null' }] }
                  movedTo: { description: 'List of applications this applicant was moved to, or null if not moved', oneOf: [{ type: array, items: { type: object } }, { type: 'null' }] }
                  movedFrom: { description: 'List of applications this applicant was moved from, or null if not moved', oneOf: [{ type: array, items: { type: object } }, { type: 'null' }] }
                  alsoConsideredForCount: { description: 'Count of other job openings this applicant is also being considered for', type: integer }
                  duplicateApplicationCount: { description: 'Count of duplicate applications from this applicant', type: integer }
                  referredBy: { description: 'Name of the person who referred this applicant, or null', oneOf: [{ type: string }, { type: 'null' }] }
                  desiredSalary: { description: "Applicant's desired salary, or null if not provided", oneOf: [{ type: string }, { type: 'null' }] }
                  commentCount: { description: 'Number of comments on this application', type: integer }
                  emailCount: { description: 'Number of emails sent for this application', type: integer }
                  eventCount: { description: 'Number of events associated with this application', type: integer }
                  questionsAndAnswers: { description: "List of custom application questions and the applicant's answers", type: array, items: { properties: { question: { properties: { id: { description: 'Question ID', type: integer }, label: { description: 'Question text', type: string } }, type: object }, answer: { properties: { id: { description: 'Answer ID', type: integer }, label: { description: 'Answer text', type: string } }, type: object }, hasRevisions: { description: 'Whether this answer has revision history', oneOf: [{ type: boolean }, { type: 'null' }] }, isArchived: { description: 'Whether this answer is archived', oneOf: [{ type: boolean }, { type: 'null' }] }, archivedDate: { description: 'ISO 8601 datetime when the answer was archived, or null', oneOf: [{ type: string, format: date-time }, { type: 'null' }] }, editedDate: { description: 'ISO 8601 datetime when the answer was last edited, or null', oneOf: [{ type: string, format: date-time }, { type: 'null' }] }, editedEndDate: { description: 'ISO 8601 datetime for the end date of the edit window, or null', oneOf: [{ type: string, format: date-time }, { type: 'null' }] } }, type: object } }
                  applicationReferences: { description: 'Free-text references provided by the applicant, or null', oneOf: [{ type: string }, { type: 'null' }] }
                  applicant: { description: 'Information about the applicant', properties: { id: { description: 'Applicant ID', type: integer }, firstName: { description: 'First name', type: string }, lastName: { description: 'Last name', type: string }, email: { description: 'Email address', type: string }, phoneNumber: { description: 'Phone number', oneOf: [{ type: string }, { type: 'null' }] }, avatar: { description: "URL to the applicant's avatar image, or null", oneOf: [{ type: string }, { type: 'null' }] }, source: { description: "Source of the application (e.g. 'LinkedIn', 'Referral'), or null", oneOf: [{ type: string }, { type: 'null' }] }, twitterUsername: { description: "Applicant's Twitter/X username, or null", oneOf: [{ type: string }, { type: 'null' }] }, address: { description: 'Applicant address', properties: { addressLine1: { oneOf: [{ type: string }, { type: 'null' }] }, addressLine2: { oneOf: [{ type: string }, { type: 'null' }] }, city: { oneOf: [{ type: string }, { type: 'null' }] }, state: { oneOf: [{ type: string }, { type: 'null' }] }, zipcode: { oneOf: [{ type: string }, { type: 'null' }] }, country: { oneOf: [{ type: string }, { type: 'null' }] } }, oneOf: [{ type: object }, { type: 'null' }] }, linkedinUrl: { description: 'LinkedIn profile URL', oneOf: [{ type: string }, { type: 'null' }] }, websiteUrl: { description: 'Personal website URL', oneOf: [{ type: string }, { type: 'null' }] }, availableStartDate: { description: "Candidate's available start date (yyyy-mm-dd)", oneOf: [{ type: string, format: date }, { type: 'null' }] }, education: { description: "Applicant's education information", properties: { institution: { description: 'Name of the educational institution', oneOf: [{ type: string }, { type: 'null' }] }, level: { description: 'Highest level of education', properties: { id: { description: 'Education level ID', type: integer }, label: { description: 'Education level name', type: string } }, oneOf: [{ type: object }, { type: 'null' }] } }, oneOf: [{ type: object }, { type: 'null' }] } }, type: object }
                  job: { description: 'Information about the job position applied to', properties: { id: { description: 'Job opening ID', type: integer }, title: { description: 'Job title', properties: { id: { description: 'Title ID, or null if not set', oneOf: [{ type: integer }, { type: 'null' }] }, label: { description: 'Title text', type: string } }, type: object }, hiringLead: { description: 'The hiring lead for this job opening', properties: { employeeId: { description: 'Employee ID of the hiring lead', type: integer }, firstName: { description: 'First name', type: string }, lastName: { description: 'Last name', type: string }, avatar: { description: 'URL to avatar image', oneOf: [{ type: string }, { type: 'null' }] }, jobTitle: { description: "Hiring lead's job title", properties: { id: { oneOf: [{ type: integer }, { type: 'null' }] }, label: { oneOf: [{ type: string }, { type: 'null' }] } }, oneOf: [{ type: object }, { type: 'null' }] } }, oneOf: [{ type: object }, { type: 'null' }] } }, type: object }
                type: object
        '401':
          description: 'Unauthorized. Invalid API credentials.'
        '403':
          description: 'Insufficient user permissions or API access is not turned on.'
        '404':
          description: 'Application not found.'
      security:
        -
          basic: []
        -
          oauth:
            - 'hiring:applications'
  '/api/v1/applicant_tracking/applications/{applicationId}/comments':
    post:
      tags:
        - 'Public API'
        - 'Applicant Tracking'
      summary: 'Create Job Application Comment'
      description: 'Add a comment to an application. The owner of the API key used must have access to ATS settings. The `type` field defaults to `comment` if omitted.'
      operationId: create-application-comment
      parameters:
        -
          name: applicationId
          in: path
          description: 'The ID of the application to add a comment to.'
          required: true
          schema:
            type: integer
      requestBody:
        description: 'Comment object to post'
        required: true
        content:
          application/json:
            schema:
              required:
                - comment
              properties:
                type:
                  description: 'The comment type. Defaults to `comment` if omitted.'
                  type: string
                comment:
                  description: 'The comment being posted.'
                  type: string
              type: object
              example:
                type: comment
                comment: 'I really like this applicant'
      responses:
        '200':
          description: 'Success. Returns the type and ID of the created comment.'
          content:
            application/json:
              schema:
                title: CreateCommentResponse
                properties:
                  type: { description: "The type of comment created, e.g. 'comment'", type: string }
                  id: { description: 'The ID of the newly created comment', type: integer }
                type: object
        '400':
          description: 'Bad request parameters.'
          content:
            application/json: {  }
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json: {  }
        '403':
          description: 'Insufficient user permissions or API access is not turned on.'
          content:
            application/json: {  }
        '404':
          description: 'Bad request url.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - 'hiring:applications.write'
  /api/v1/applicant_tracking/jobs:
    get:
      tags:
        - 'Public API'
        - 'Applicant Tracking'
      summary: 'Get Job Summaries'
      description: 'Get a list of job opening summaries. The owner of the API key used must have access to ATS settings. Results can be filtered by status group and sorted by various fields. By default returns all non-deleted job openings.'
      operationId: get-job-summaries
      parameters:
        -
          name: statusGroups
          in: query
          description: 'One or more status groups to filter positions by, comma-separated (e.g. `Draft,Open`). Allowed values: `ALL`, `DRAFT_AND_OPEN`, `Open`, `Filled`, `Draft`, `Deleted`, `On Hold`, `Canceled`. Defaults to all non-deleted positions.'
          schema:
            type: string
        -
          name: status_ids
          in: query
          description: 'One or more specific job opening status IDs to filter by, comma-separated (e.g. `1,2`). Combined with `statusGroups` when both are provided.'
          schema:
            type: string
        -
          name: sortBy
          in: query
          description: 'A specific field to sort the results by.'
          schema:
            type: string
            enum:
              - count
              - title
              - lead
              - created
              - status
        -
          name: sortOrder
          in: query
          description: 'Order by which to sort results.'
          schema:
            type: string
            enum:
              - ASC
              - DESC
      responses:
        '200':
          description: 'Success. Returns an array of job opening summary objects.'
          content:
            application/json:
              schema:
                title: JobSummariesList
                type: array
                items:
                  title: JobSummary
                  properties: { id: { description: 'Job opening ID', type: integer }, title: { description: 'Job title', properties: { id: { description: 'Title ID, or null if not set', oneOf: [{ type: integer }, { type: 'null' }] }, label: { description: 'Title text', type: string } }, type: object }, postedDate: { description: 'ISO 8601 datetime when the job opening was posted', type: string, format: date-time }, location: { description: 'Location information', properties: { id: { description: 'Location ID', type: integer }, label: { description: 'Location display name', type: string }, address: { description: 'Full address of the location', type: object } }, oneOf: [{ type: object }, { type: 'null' }] }, department: { description: 'Department information', properties: { id: { description: 'Department ID', type: integer }, label: { description: 'Department name', type: string } }, oneOf: [{ type: object }, { type: 'null' }] }, status: { description: 'Job opening status', properties: { id: { description: 'Status ID', type: integer }, label: { description: 'Status name', type: string } }, type: object }, hiringLead: { description: 'The hiring lead for this job opening', properties: { employeeId: { description: 'Employee ID', type: integer }, firstName: { description: 'First name', type: string }, lastName: { description: 'Last name', type: string }, avatar: { description: 'URL to avatar image', oneOf: [{ type: string }, { type: 'null' }] }, jobTitle: { description: "Hiring lead's job title", oneOf: [{ type: object }, { type: 'null' }] } }, oneOf: [{ type: object }, { type: 'null' }] }, newApplicantsCount: { description: "Number of applicants in 'New' status", type: integer }, activeApplicantsCount: { description: 'Number of applicants in an active status', type: integer }, totalApplicantsCount: { description: 'Total number of applicants', type: integer }, postingUrl: { description: 'Public URL for the job posting, or null if not posted', oneOf: [{ type: string }, { type: 'null' }] } }
                  type: object
        '400':
          description: 'Bad request parameters.'
          content:
            application/json: {  }
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json: {  }
        '403':
          description: 'Insufficient user permissions or API access is not turned on.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - 'hiring:applications'
  '/api/v1/applicant_tracking/applications/{applicationId}/status':
    post:
      tags:
        - 'Public API'
        - 'Applicant Tracking'
      summary: 'Update Applicant Status'
      description: 'Update the status of an application. The owner of the API key used must have access to ATS settings. Use the Get Applicant Statuses endpoint to obtain valid status IDs.'
      operationId: update-applicant-status
      parameters:
        -
          name: applicationId
          in: path
          description: 'The ID of the application whose status should be updated.'
          required: true
          schema:
            type: integer
      requestBody:
        description: 'The new status to assign to the application.'
        required: true
        content:
          application/json:
            schema:
              required:
                - status
              properties:
                status:
                  description: 'The ID of the status to assign to the application. Use the Get Applicant Statuses endpoint to get available status IDs.'
                  type: integer
              type: object
              example:
                status: 2
      responses:
        '200':
          description: 'Success. Returns the type and ID of the status change record.'
          content:
            application/json:
              schema:
                title: UpdateApplicantStatusResponse
                properties:
                  type: { description: "Always 'positionApplicantStatus'", type: string }
                  id: { description: 'The ID of the created status change record', type: integer }
                type: object
        '400':
          description: 'Bad request. The error message is returned in the `x-bamboohr-error-message` response header; the body is empty.'
          headers:
            x-bamboohr-error-message:
              description: "Human-readable error message describing why the request failed (e.g. 'Invalid status')."
              schema:
                type: string
          content:
            text/html: {  }
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json: {  }
        '403':
          description: 'Insufficient user permissions or API access is not turned on.'
          content:
            application/json: {  }
        '404':
          description: 'Bad request url.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - 'hiring:applications.write'
  /api/v1/benefitcoverages:
    get:
      tags:
        - Benefits
        - 'Public API'
      summary: 'List Benefit Coverages'
      description: 'Returns all benefit coverage levels configured in the company, such as Employee Only, Employee + Spouse, and Employee + Family. The JSON response wraps results under a "Benefit Coverages" key. Each coverage level includes an ID, short name, optional description, sort order, and an associated benefit plan ID (null for company-wide levels). Requires Benefits Administration permissions or owner/admin access.'
      operationId: list-benefit-coverages
      parameters:
        -
          $ref: '#/components/parameters/AcceptHeaderParameter'
      responses:
        '200':
          description: 'A collection of benefit coverage levels ordered by sort order.'
          content:
            application/json:
              schema:
                title: BenefitCoveragesResponse
                properties:
                  'Benefit Coverages': { description: 'Array of benefit coverage level objects.', type: array, items: { properties: { id: { description: 'The coverage level ID.', type: string }, shortName: { description: 'The short display name for this coverage level (e.g. "Employee + Spouse").', type: string }, description: { description: 'An optional longer description of the coverage level. Null if not set.', oneOf: [{ type: string }, { type: 'null' }] }, sortOrder: { description: 'The display sort order for this coverage level.', type: string }, benefitPlanId: { description: 'The benefit plan this coverage level belongs to, or null for company-wide coverage levels.', oneOf: [{ type: string }, { type: 'null' }] } }, type: object } }
                type: object
            application/xml: {  }
        '403':
          description: 'The authenticated user does not have Benefits Administration permissions.'
      security:
        -
          basic: []
        -
          oauth:
            - benefit
  '/api/v1/employeedependents/{id}':
    get:
      tags:
        - Benefits
        - 'Public API'
      summary: 'Get Employee Dependent'
      description: 'Returns the details of a single employee dependent by their dependent ID. The response is a JSON object with a top-level key "Employee Dependents" containing a single-element array. SSN and SIN are returned as masked values (e.g. "xxx-xx-1234"). State and country are returned as full names. Supports both JSON and XML response formats via the Accept header. Requires Benefits Administration permissions.'
      operationId: get-employee-dependent
      parameters:
        -
          $ref: '#/components/parameters/AcceptHeaderParameter'
        -
          name: id
          in: path
          description: 'The numeric ID of the employee dependent to retrieve.'
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'The requested employee dependent.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmployeeDependentsResponse'
            application/xml: {  }
        '400':
          description: 'The specified dependent ID was not found.'
        '403':
          description: 'The authenticated user does not have Benefits Administration permissions.'
      security:
        -
          basic: []
        -
          oauth:
            - 'employee:dependent:ssn'
        -
          oauth:
            - 'employee:dependent'
    put:
      tags:
        - Benefits
        - 'Public API'
      summary: 'Update Employee Dependent'
      description: 'Replaces all fields on an existing employee dependent record. The request body must contain the full desired state of the dependent — omitted fields are written as empty or null, not preserved. `employeeId` is required and must reference a valid employee. `relationship` must be a valid relationship type and `gender` must be a valid gender value. `isUsCitizen` and `isStudent` accept "yes" or "no". `state` accepts a state code (e.g. "UT") and `country` accepts an ISO 3166-1 alpha-2 country code (e.g. "US"). `dateOfBirth` must be in YYYY-MM-DD format. SSN and SIN are accepted as plain text and stored encrypted. Accepts both `application/json` and `application/xml` request bodies. The response format mirrors the request `Content-Type` (not the `Accept` header): JSON request bodies receive a JSON response; XML request bodies receive an XML response. A successful update fires an internal dependent-updated event that may trigger downstream benefit enrollment processing.'
      operationId: update-employee-dependent
      parameters:
        -
          name: id
          in: path
          description: 'The numeric ID of the employee dependent to update.'
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EmployeeDependent'
          application/xml:
            schema:
              $ref: '#/components/schemas/EmployeeDependent'
      responses:
        '200':
          description: 'The updated dependent record. Format matches the request Content-Type (JSON or XML).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmployeeDependentsResponse'
            application/xml: {  }
        '400':
          description: 'The request body is invalid or contains a validation error (e.g. invalid relationship, gender, state code, country code, or date format).'
        '403':
          description: 'The authenticated user does not have permission to update this dependent.'
        '409':
          description: 'A duplicate record conflict was detected.'
        '500':
          description: 'An unexpected server error occurred.'
      security:
        -
          basic: []
        -
          oauth:
            - 'employee:dependent:ssn.write'
        -
          oauth:
            - 'employee:dependent.write'
  /api/v1/employeedependents:
    get:
      tags:
        - Benefits
        - 'Public API'
      summary: 'List Employee Dependents'
      description: 'Returns employee dependents for the company. When `employeeid` is provided, only dependents for that employee are returned. When omitted, all dependents across all employees are returned. The response is a JSON object with a top-level key "Employee Dependents" containing an array of dependent objects. SSN and SIN are returned as masked values (e.g. "xxx-xx-1234"). State and country are returned as full names. Supports both JSON and XML response formats via the Accept header. Requires Benefits Administration permissions.'
      operationId: list-employee-dependents
      parameters:
        -
          $ref: '#/components/parameters/AcceptHeaderParameter'
        -
          name: employeeid
          in: query
          description: 'The employee ID to filter dependents by. When provided, only dependents for that employee are returned. When omitted, all company dependents are returned.'
          required: false
          schema:
            type: integer
      responses:
        '200':
          description: 'A collection of employee dependents.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmployeeDependentsResponse'
            application/xml: {  }
        '403':
          description: 'The authenticated user does not have Benefits Administration permissions.'
      security:
        -
          basic: []
        -
          oauth:
            - 'employee:dependent:ssn'
        -
          oauth:
            - 'employee:dependent'
    post:
      tags:
        - Benefits
        - 'Public API'
      summary: 'Create Employee Dependent'
      description: 'Creates a new dependent record for an employee. `employeeId` is required and must reference a valid employee. `relationship` must be a valid relationship type and `gender` must be a valid gender value. `isUsCitizen` and `isStudent` accept "yes" or "no". `state` accepts a state code (e.g. "UT") and `country` accepts an ISO 3166-1 alpha-2 country code (e.g. "US"). `dateOfBirth` must be in YYYY-MM-DD format. SSN and SIN are accepted as plain text and stored encrypted. Accepts both `application/json` and `application/xml` request bodies. The response format mirrors the request `Content-Type` (not the `Accept` header): JSON request bodies receive a JSON response; XML request bodies receive an XML response. A successful creation fires an internal dependent-created event that may trigger downstream benefit enrollment processing.'
      operationId: create-employee-dependent
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EmployeeDependent'
          application/xml:
            schema:
              $ref: '#/components/schemas/EmployeeDependent'
      responses:
        '200':
          description: 'The newly created dependent record. Format matches the request Content-Type (JSON or XML).'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/EmployeeDependentsResponse'
            application/xml: {  }
        '400':
          description: 'The request body is invalid or contains a validation error (e.g. missing employeeId, invalid relationship, gender, state code, country code, or date format).'
        '403':
          description: 'The authenticated user does not have permission to add a dependent.'
        '409':
          description: 'A duplicate record conflict was detected.'
        '500':
          description: 'An unexpected server error occurred.'
      security:
        -
          basic: []
        -
          oauth:
            - 'employee:dependent:ssn.write'
        -
          oauth:
            - 'employee:dependent.write'
  /api/v1/employees/directory:
    get:
      tags:
        - Employees
        - 'Public API'
      summary: 'Get Employees Directory'
      description: "Returns the company's published employee directory: a fieldset definition plus an array of employee records whose keys match the field ids. Coverage is intentionally incomplete — companies can hide individual employees (commonly contractors) from the directory, and those employees are silently absent from the response with no indicator that they exist. The fieldset is also fixed by company directory configuration; callers cannot request additional fields. Use this endpoint only when you specifically want the company's curated directory subset (for example, an org-style listing that respects the company's directory settings). For complete or general-purpose employee listings, batch lookups by ID, or filtering by name/status, use **List Employees** (`list-employees`) instead. For tabular reporting, custom field selection, or analytical queries across many employees, use **Get Data from Dataset (v2)** (`get-data-from-dataset-v2`) instead. Response format follows the `Accept` header: `application/json` returns a JSON object with `fields` and `employees` arrays; `application/xml` (the default when `Accept` is missing or any non-JSON value) returns a `<directory>` document with `<fieldset>` and `<employees>` children. Employee `id` values are returned as strings in both formats. The response shape varies by caller permission and per-company configuration: when neither company-wide directory sharing nor org-chart sharing is enabled the endpoint returns 403; when only org-chart sharing is enabled the response uses a reduced fieldset (limited to the fields exposed by the org chart); when the resulting directory has no employees the endpoint returns 404 rather than an empty list."
      operationId: get-employees-directory
      parameters:
        -
          $ref: '#/components/parameters/AcceptHeaderParameter'
        -
          name: onlyCurrent
          in: query
          description: 'When true (the default), only employees whose hire date and employment-status effective date are on or before today are returned. Set to false to also include employees with a future hire date or future employment-status effective date (typically pre-boarding hires). The fieldset is unaffected.'
          required: false
          schema:
            type: boolean
            default: true
      responses:
        '200':
          description: 'Directory fieldset and matching employee records, encoded per the `Accept` header.'
          content:
            application/json:
              schema:
                title: EmployeesDirectoryJsonResponse
                properties:
                  fields: { description: 'Field definitions included in this response. Each entry describes one key that appears on every employee object. The set of fields is fixed by company directory configuration; the `canUploadPhoto` entry is always appended.', type: array, items: { properties: { id: { description: 'Field identifier; appears as a key on each employee object.', type: string }, type: { description: 'Field data type. Observed values include `text`, `list`, `email`, `bool`, `url`, and `employee`.', type: string }, name: { description: 'Human-readable field label.', type: string } }, type: object } }
                  employees: { description: 'One object per directory employee. Every object includes `id` (employee ID as a string) and `canUploadPhoto` (1 if the authenticated caller may upload a photo for this employee, 0 otherwise), plus one key per entry in `fields`. The exact key set varies with company directory configuration. Field values are returned in the JSON type indicated by the matching `fields[].type` (for example, `bool` fields are JSON booleans).', type: array, items: { type: object, additionalProperties: true } }
                type: object
            application/xml:
              schema:
                title: EmployeesDirectoryXmlResponse
                properties:
                  fieldset: { description: 'Field definitions included in this response.', properties: { field: { type: array, items: { properties: { id: { description: 'Field identifier; matches the `id` attribute on each `<field>` element inside `<employee>`.', type: string, xml: { attribute: true } }, value: { description: "Human-readable field label, serialized as the element's text content.", type: string } }, type: object } } }, type: object }
                  employees: { description: 'Directory employee records.', properties: { employee: { type: array, items: { properties: { id: { description: 'Employee ID, serialized as an attribute on the `<employee>` element.', type: string, xml: { attribute: true } }, field: { description: 'Field values for the employee. Each `<field>` element has an `id` attribute matching one of the `<fieldset>` ids and the value as element text. Currency fields additionally carry a `currency` attribute (ISO currency code). Boolean fields are serialized as the strings `true`/`false`; the appended `canUploadPhoto` field is serialized as `yes`/`no`.', type: array, items: { properties: { id: { description: 'Field identifier matching one of the ids in `<fieldset>`.', type: string, xml: { attribute: true } }, value: { description: 'Field value serialized as element text content.', type: string } }, type: object } } }, type: object } } }, type: object }
                type: object
                xml:
                  name: directory
        '401':
          description: Unauthorized.
        '403':
          description: "The authenticated user's company has neither company-wide directory sharing nor org-chart sharing enabled, so no directory is available."
        '404':
          description: 'The resulting directory is empty (no employees matched the filter for this company configuration).'
        '500':
          description: 'Internal server error.'
      security:
        -
          basic: []
        -
          oauth:
            - employee_directory
  /api/v1/files/view:
    get:
      tags:
        - 'Company Files'
        - 'Public API'
      summary: 'Get Company Files and Categories'
      description: 'Returns all company file categories and the files within each category that the requesting user is permitted to see. The response format is determined by the `Accept` request header: send `application/json` for JSON or omit it (or send `application/xml`) for XML.'
      operationId: list-company-files
      responses:
        '200':
          description: 'List of company file categories and their visible files.'
          content:
            application/json:
              schema:
                title: CompanyFilesResponse
                properties:
                  categories: { description: 'List of company file categories.', type: array, items: { properties: { id: { description: 'Category ID.', type: integer }, name: { description: 'Category display name.', type: string }, canUploadFiles: { description: 'Whether the requesting user can upload files to this category.', type: string, enum: ['yes', 'no'] }, files: { description: 'Files visible to the requesting user in this category.', type: array, items: { properties: { id: { description: 'File ID.', type: integer }, name: { description: 'File display name.', type: string }, originalFileName: { description: 'Original uploaded filename including extension.', type: string }, size: { description: 'File size in bytes.', type: string }, dateCreated: { description: 'Timestamp of when the file was uploaded, in ISO 8601 format with UTC offset (e.g. `2025-02-11T22:30:07+0000`).', type: string }, createdBy: { description: 'Full name of the user who uploaded the file.', type: string }, shareWithEmployees: { description: 'Whether the file is shared with all employees.', type: string, enum: ['yes', 'no'] }, canRenameFile: { description: 'Whether the requesting user can rename this file.', type: string, enum: ['yes', 'no'] }, canDeleteFile: { description: 'Whether the requesting user can delete this file.', type: string, enum: ['yes', 'no'] } }, type: object } } }, type: object } }
                type: object
            application/xml: {  }
        '403':
          description: 'The API user does not have permission to access company files.'
      security:
        -
          basic: []
        -
          oauth:
            - company_file
  '/api/v1/employees/{id}/files/view':
    get:
      tags:
        - 'Employee Files'
        - 'Public API'
      summary: 'List Employee Files'
      description: "Lists the file categories and files visible to the caller for the specified employee. This is a metadata listing (names, sizes, permissions); to download a file's content use `get-employee-file`. The response format is controlled by the Accept header: send `application/json` for JSON or omit/send anything else for XML. Only categories and files the caller is permitted to see are included; employees viewing their own profile also see files shared with them. Returns 404 when the employee has no accessible categories."
      operationId: list-employee-files
      parameters:
        -
          name: id
          in: path
          description: 'The ID of the employee whose files are being listed.'
          required: true
          schema:
            type: integer
        -
          name: Accept
          in: header
          description: 'Set to `application/json` to receive a JSON response. Any other value (or omitted) returns XML.'
          required: false
          schema:
            type: string
            default: application/xml
      responses:
        '200':
          description: "The employee's file categories and files. Each category contains permission flags and an array of file metadata."
          content:
            application/json:
              schema:
                title: JsonEmployeeFiles
                properties:
                  employee: { properties: { id: { description: 'Employee ID.', type: integer } }, type: object }
                  categories: { type: array, items: { properties: { id: { description: 'Category ID.', type: integer }, name: { description: 'Category display name.', type: string }, canRenameCategory: { description: 'Whether the caller can rename this category.', type: string, enum: ['yes', 'no'] }, canDeleteCategory: { description: 'Whether the caller can delete this category.', type: string, enum: ['yes', 'no'] }, canUploadFiles: { description: 'Whether the caller can upload files to this category.', type: string, enum: ['yes', 'no'] }, displayIfEmpty: { description: 'Whether to display the category when it has no files.', type: string, enum: ['yes', 'no'] }, files: { type: array, items: { properties: { id: { description: 'File ID.', type: integer }, name: { description: 'File display name.', type: string }, originalFileName: { description: 'Original uploaded filename.', type: string }, size: { description: 'File size in bytes.', type: integer }, dateCreated: { description: 'UTC creation timestamp in ISO 8601 format (e.g. `2024-01-15T12:30:00+0000`).', type: string }, createdBy: { description: 'Full name of the user who uploaded the file.', type: string }, shareWithEmployee: { description: 'Whether the file is shared with the employee.', type: string, enum: ['yes', 'no'] }, canRenameFile: { description: 'Whether the caller can rename this file.', type: string, enum: ['yes', 'no'] }, canDeleteFile: { description: 'Whether the caller can delete this file.', type: string, enum: ['yes', 'no'] }, canChangeShareWithEmployeeFieldValue: { description: 'Whether the caller can toggle the share-with-employee setting.', type: string, enum: ['yes', 'no'] } }, type: object } } }, type: object } }
                type: object
            application/xml:
              schema:
                title: XmlEmployeeFiles
                properties:
                  id: { description: 'Employee ID on the root `<employee>` element.', type: string, xml: { attribute: true } }
                  category: { type: array, items: { properties: { id: { description: 'Category ID on the `<category>` element.', type: string, xml: { attribute: true } }, name: { type: string }, canRenameCategory: { type: string, enum: ['yes', 'no'] }, canDeleteCategory: { type: string, enum: ['yes', 'no'] }, canUploadFiles: { type: string, enum: ['yes', 'no'] }, displayIfEmpty: { type: string, enum: ['yes', 'no'] }, file: { type: array, items: { properties: { id: { description: 'File ID on the `<file>` element.', type: string, xml: { attribute: true } }, name: { type: string }, originalFileName: { type: string }, size: { description: 'File size in bytes (serialized as string in XML).', type: string }, dateCreated: { description: 'UTC creation timestamp in `YYYY-MM-DD HH:MM:SS` format.', type: string }, createdBy: { type: string }, shareWithEmployee: { type: string, enum: ['yes', 'no'] }, canRenameFile: { type: string, enum: ['yes', 'no'] }, canDeleteFile: { type: string, enum: ['yes', 'no'] }, canChangeShareWithEmployeeFieldValue: { type: string, enum: ['yes', 'no'] } }, type: object } } }, type: object } }
                type: object
                xml:
                  name: employee
        '403':
          description: "The API user does not have permission to access the requested employee's files."
        '404':
          description: 'The requested employee was not found, or the employee has no accessible file categories.'
      security:
        -
          basic: []
        -
          oauth:
            - 'employee:file'
  /api/v1/login:
    post:
      tags:
        - Login
        - 'Public API'
      summary: Login
      description: "Exchanges username, password, and application key for a persistent API key scoped to that user and application. No pre-existing authentication is required; credentials are passed in the request body. On success, returns a persistent API key, the authenticated user ID, the linked employee ID (null when no employee record is associated), and the base API URL to use for subsequent requests.\n\nThis endpoint is deprecated. New integrations should prefer OAuth or OpenID Connect instead.\n\n`applicationKey` must correspond to a registered non-mobile application; iOS and Android app keys are explicitly rejected with a 403 (no body). The optional `deviceId` associates the generated key with a specific device.\n\nResponse format is determined by the `Accept` request header. Send `Accept: application/json` to receive JSON; omit the header or send any other value to receive XML. Alternatively, set `?format=json` in the query string to force JSON regardless of the `Accept` header.\n\nNote: If the company has SSO enabled and password login is disabled, this endpoint returns HTTP 200 with a plain-text error message rather than a structured error response."
      operationId: login
      parameters:
        -
          $ref: '#/components/parameters/AcceptHeaderParameter'
        -
          name: format
          in: query
          description: 'When set to `json`, forces a JSON response regardless of the `Accept` header.'
          required: false
          schema:
            type: string
            enum:
              - json
      requestBody:
        required: true
        content:
          application/x-www-form-urlencoded:
            schema:
              $ref: '#/components/schemas/Login'
      responses:
        '200':
          description: 'Successful authentication.'
          content:
            application/json:
              schema:
                title: LoginResponse
                required:
                  - success
                  - userId
                  - key
                  - apiUrl
                properties:
                  success: { description: 'Always `true` on HTTP 200.', type: boolean }
                  userId: { description: 'The authenticated user ID.', type: integer }
                  employeeId: { description: 'The employee ID linked to this user. `null` when no employee record is associated.', oneOf: [{ type: integer }, { type: 'null' }] }
                  key: { description: 'The API key to use for subsequent requests.', type: string }
                  apiUrl: { description: 'The base URL for subsequent API calls.', type: string, format: uri }
                type: object
            application/xml:
              schema:
                title: LoginXmlResponse
                properties:
                  response: { description: 'Always `authenticated` on HTTP 200.', type: string }
                  userId: { description: 'The authenticated user ID.', type: integer }
                  employeeId: { description: 'The employee ID linked to this user. Rendered as `<employeeId isNull="yes"/>` when no employee record is associated.', type: integer }
                  key: { description: 'The API key to use for subsequent requests.', type: string }
                  apiUrl: { description: 'The base URL for subsequent API calls.', type: string, format: uri }
                type: object
                xml:
                  name: auth
        '403':
          description: 'Forbidden. Credential or key failure. Credential and key failures include a response body; mobile application key rejections return an empty body.'
          content:
            application/json:
              schema:
                title: LoginFailureResponse
                required:
                  - success
                properties:
                  success: { description: 'Always `false`.', type: boolean }
                type: object
            application/xml:
              schema:
                title: LoginFailureXmlResponse
                properties:
                  response: { description: 'Always `declined`.', type: string }
                type: object
                xml:
                  name: auth
        '500':
          description: 'Internal Server Error. API key creation failed.'
          content:
            application/json:
              schema:
                title: LoginFailureResponse
                required:
                  - success
                properties:
                  success: { description: 'Always `false`.', type: boolean }
                type: object
            application/xml:
              schema:
                title: LoginFailureXmlResponse
                properties:
                  response: { description: 'Always `declined`.', type: string }
                type: object
                xml:
                  name: auth
      deprecated: true
      security: []
  /api/v1/meta/lists:
    get:
      tags:
        - 'Account Information'
        - 'Public API'
      summary: 'List List Fields'
      description: 'Returns details for all list fields in the account. Each list includes its field ID, alias, options, and whether it is manageable (editable). Lists with the `manageable` attribute set to `yes` can be modified via the PUT endpoint. Lists with the `multiple` attribute set to `yes` are fields that can have multiple values. Options with the `archived` attribute set to `yes` are soft-deleted and included so that historical data can reference the value — filter by `archived: no` to show only active options to end users.'
      operationId: list-list-fields
      parameters:
        -
          name: format
          in: query
          description: 'Set to "json" to receive JSON output as an alternative to using the Accept header.'
          required: false
          schema:
            type: string
            enum:
              - json
        -
          $ref: '#/components/parameters/AcceptHeaderParameter'
      responses:
        '200':
          description: 'All list field details, including field IDs, option values, and manageability.'
          content:
            application/json:
              schema:
                title: ListFieldsResponse
                type: array
                items:
                  $ref: '#/components/schemas/ListFieldDetail'
            application/xml:
              schema:
                title: ListFieldsXmlResponse
                type: array
                items:
                  $ref: '#/components/schemas/ListFieldDetail'
                xml:
                  name: lists
                  wrapped: true
                example: "<?xml version=\"1.0\"?>\n<lists>\n  <list id=\"3\" fieldId=\"1\" alias=\"department\" manageable=\"yes\" multiple=\"no\">\n    <name>Department</name>\n    <options>\n      <option id=\"45\" archived=\"no\" createdDate=\"2022-06-01T12:00:00+00:00\" archivedDate=\"\">Engineering</option>\n      <option id=\"46\" archived=\"yes\" createdDate=\"2020-01-15T09:00:00+00:00\" archivedDate=\"2023-03-10T14:00:00+00:00\">Legacy Team</option>\n    </options>\n  </list>\n</lists>"
        '403':
          description: 'The API user does not have permission to view employee fields.'
      security:
        -
          basic: []
        -
          oauth:
            - field
  '/api/v1/meta/lists/{listFieldId}':
    put:
      tags:
        - 'Account Information'
        - 'Public API'
      summary: 'Update List Field Values'
      description: 'Create, update, or archive options for a list field. To update an existing option, specify its `id`. To create a new option, omit `id`. To archive an option, set `archived` to `yes` — the option is soft-deleted and will continue to appear in GET responses for historical data integrity. To reactivate an archived option, set `archived` to `no`. The `archivedDate` field is server-set when an option is first archived and is not cleared if the option is later reactivated. Options on list fields with `manageable: no` cannot be modified and will return a 405.'
      operationId: update-list-field-values
      parameters:
        -
          name: listFieldId
          in: path
          description: 'The field ID of the list to update. This is the `fieldId` value returned by the GET endpoint, not the list `id`.'
          required: true
          schema:
            type: string
        -
          name: format
          in: query
          description: 'Set to "json" to receive JSON output as an alternative to using the Accept header.'
          required: false
          schema:
            type: string
            enum:
              - json
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ListFieldValues'
          text/xml:
            schema:
              $ref: '#/components/schemas/ListFieldValuesXml'
      responses:
        '200':
          description: 'All requested changes were applied. Returns the full updated list.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ListFieldDetail'
            text/xml:
              schema:
                $ref: '#/components/schemas/ListFieldDetail'
        '400':
          description: 'The request body is invalid or malformed.'
        '403':
          description: 'The list is not editable, or the API user does not have sufficient permissions.'
        '404':
          description: 'The specified list field or option ID does not exist.'
        '405':
          description: 'One or more of the specified options belong to a non-manageable list field and cannot be modified.'
        '409':
          description: 'A duplicate list value conflicted with the value specified.'
      security:
        -
          basic: []
        -
          oauth:
            - field.write
  /api/v1/meta/tables:
    get:
      tags:
        - 'Account Information'
        - 'Public API'
      summary: 'List Tabular Fields'
      description: 'Returns a list of all tabular (table-based) fields available in the account. Each table includes its alias and the fields it contains with their IDs, names, and types. Use this endpoint to discover which table names are valid for the table row endpoints (e.g., jobInfo, compensation, employmentStatus). For fields whose type is `list`, `multilist`, or another option-backed type, the field `id` can be matched to `fieldId` from `list-list-fields` to retrieve the account-level option list.'
      operationId: list-tabular-fields
      parameters:
        -
          $ref: '#/components/parameters/AcceptHeaderParameter'
      responses:
        '200':
          description: "A list of all tabular fields in the account, with each table's fields and their metadata."
          content:
            application/json:
              schema:
                title: TabularFieldsResponse
                type: array
                items:
                  title: TabularField
                  properties: { alias: { description: 'The API alias for the table (e.g., jobInfo, compensation).', type: string }, fields: { description: 'The fields in this table.', type: array, items: { properties: { id: { description: 'The field ID.', type: integer }, name: { description: 'The display name of the field.', type: string }, alias: { description: 'The API alias for the field within this table.', type: string }, type: { description: 'The data type of the field (e.g., text, date, list).', type: string } }, type: object } } }
                  type: object
            application/xml: {  }
      security:
        -
          basic: []
        -
          oauth:
            - field
  '/api/v1/reports/{id}':
    get:
      tags:
        - Reports
        - 'Public API'
      summary: 'Get Company Report'
      description: "**Deprecated. Use Custom Reports > Get Report by ID instead.**\n\nReturns the data from an existing saved custom report. Non-admins can find these reports in My Reports. Admins can find them in Custom Reports under the My Reports and Company Reports tabs. The report ID can be found by hovering over the report name in BambooHR and noting the ID in the URL. Standard Reports are not available via this endpoint, and report IDs are company-specific.\n\nThe caller must have permission to view or access the report. The `format` query parameter is case-insensitive (`json`, `JSON`, `Json` are all accepted). If `format` is omitted, the output format is inferred from the `Accept` header, but only these exact values are supported: `application/json`, `text/xml`, `text/csv`, `application/pdf`, `application/vnd.ms-excel`. Any other `Accept` value (including `application/xml` and `*/*`) returns 404."
      operationId: get-company-report
      parameters:
        -
          name: Accept
          in: header
          description: 'The desired response content type when `format` is omitted. Accepted values: `application/json`, `text/xml`, `text/csv`, `application/pdf`, `application/vnd.ms-excel`. Any other value returns 404.'
          schema:
            type: string
            enum:
              - application/json
              - text/xml
              - text/csv
              - application/pdf
              - application/vnd.ms-excel
        -
          name: id
          in: path
          description: 'The integer ID of the saved custom report to run. Find this ID by hovering over the report name in the BambooHR Reports tab and noting the ID in the URL.'
          required: true
          schema:
            type: integer
        -
          name: format
          in: query
          description: 'The output format for the report. Case-insensitive. If omitted, format is inferred from the `Accept` header — only `application/json`, `text/xml`, `text/csv`, `application/pdf`, and `application/vnd.ms-excel` are accepted; any other value returns 404.'
          required: false
          schema:
            type: string
            enum:
              - json
              - xml
              - csv
              - xls
              - pdf
        -
          name: fd
          in: query
          description: 'Controls duplicate row filtering. `yes` applies standard deduplication (default for JSON and XML formats). `no` returns raw results without filtering (default for CSV and XLS formats).'
          schema:
            type: string
            enum:
              - 'yes'
              - 'no'
        -
          name: onlyCurrent
          in: query
          description: 'Whether to restrict historical fields to current values only. Set to false to include future-dated history values in the report output. Defaults to true.'
          schema:
            type: boolean
            default: true
      responses:
        '200':
          description: 'Report data in the requested format. For JSON, returns an object with a `title` string, a `fields` array (each element has `id`, `type`, and `name`), and an `employees` array (each element has `id` plus one key per report field). For XML (`text/xml`), returns a `<report>` document. For CSV/XLS/PDF, returns file content with the appropriate content-type header.'
          content:
            application/json:
              schema:
                title: GetCompanyReportResponse
                properties:
                  title: { description: 'The report title.', type: string }
                  fields: { description: 'Metadata for each field included in the report.', type: array, items: { properties: { id: { description: 'The field ID.', type: string }, type: { description: 'The field data type.', type: string }, name: { description: 'The human-readable field label.', type: string } }, type: object } }
                  employees: { description: 'One object per employee. Each object contains an `id` property plus one key per report field.', type: array, items: { properties: { id: { description: 'The employee ID.', type: string } }, type: object, additionalProperties: { type: string } } }
                type: object
            text/xml: {  }
            text/csv: {  }
            application/pdf: {  }
            application/vnd.ms-excel: {  }
        '403':
          description: 'Access denied. The authenticated user does not have permission to view this report.'
        '404':
          description: 'Not found. Returned when the report ID does not exist or belongs to a different company, when an unsupported `format` value is supplied (e.g. `?format=bogus`), or when `format` is omitted and the `Accept` header is not one of the supported exact values (`application/json`, `text/xml`, `text/csv`, `application/pdf`, `application/vnd.ms-excel`).'
      deprecated: true
      security:
        -
          basic: []
        -
          oauth:
            - report
  '/api/v1/employees/{employeeId}/time_off/calculator':
    get:
      tags:
        - 'Time Off'
        - 'Public API'
      summary: 'Get Time Off Balance'
      description: "Returns time off balances for an employee across all assigned categories as of a given date. Each category's balance is calculated by summing all historical balance events (accruals, manual adjustments, used time off, and carry-over events) plus any future accruals and adjustments up to the specified date. To get current balances, pass today's date; to project future balances, pass a future date. Response defaults to XML unless Accept: application/json is provided."
      operationId: get-time-off-balance
      parameters:
        -
          $ref: '#/components/parameters/AcceptHeaderParameter'
        -
          name: end
          in: query
          description: 'The date to calculate the time off balance as of, in YYYY-MM-DD format. Defaults to company today if not provided. Example: use a future date to project balance.'
          required: false
          schema:
            type: string
            format: date
          example: '2026-12-31'
        -
          name: precision
          in: query
          description: 'Number of decimal places for balance and usedYearToDate values. Minimum 0, maximum 4. Defaults to 2.'
          required: false
          schema:
            type: integer
            default: 2
            maximum: 4
            minimum: 0
          example: 2
        -
          name: employeeId
          in: path
          description: 'The ID of the employee to get time off balances for.'
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'An array of time off balance entries, one per assigned time off type.'
          content:
            application/json:
              schema:
                title: TimeOffBalanceResponse
                type: array
                items:
                  title: TimeOffBalanceEntry
                  properties: { timeOffType: { description: 'The ID of the time off type.', type: string }, name: { description: 'The name of the time off type.', type: string }, units: { description: "The unit of measurement for the balance (e.g. 'hours' or 'days').", type: string, example: hours }, balance: { description: 'The calculated time off balance as of the specified date, rounded using `precision`.', type: string, example: '24.50' }, end: { description: 'The date the balance was calculated as of, in YYYY-MM-DD format.', type: string, format: date }, policyType: { description: 'The type of time off policy assigned to the employee for this time off type.', type: string, enum: [accruing, discretionary, manual] }, usedYearToDate: { description: 'The amount of time off used year-to-date as of the specified date, rounded using `precision`.', type: string, example: '12.25' } }
                  type: object
            application/xml: {  }
        '401':
          description: 'Unauthorized. Invalid API credentials.'
        '404':
          description: 'Employee not found. Some clients may receive an empty HTML response body for this legacy error case.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProblemDetailsResponse'
      security:
        -
          basic: []
        -
          oauth:
            - time_off
  '/api/v1/employees/{employeeId}/time_off/history':
    put:
      tags:
        - 'Time Off'
        - 'Public API'
      summary: 'Create Time Off History Item'
      description: 'Creates a time off history item for an employee. For `used` type entries, a `timeOffRequestId` referencing an approved request is required. For `override` (balance adjustment) entries via the /history path, provide the `amount` and `timeOffTypeId` directly. The `eventType` defaults based on the URI path when omitted.'
      operationId: create-time-off-history
      parameters:
        -
          name: employeeId
          in: path
          description: 'The ID of the employee.'
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TimeOffHistory'
          application/xml:
            schema:
              $ref: '#/components/schemas/TimeOffHistory'
      responses:
        '201':
          description: 'The history item has been created.'
          content:
            application/json: {  }
        '400':
          description: 'Empty or malformed JSON/XML, an invalid date format, an invalid event type, an invalid time off request, an invalid time off type, or an invalid override amount.'
          content:
            application/json: {  }
        '401':
          description: 'Unauthorized. Invalid API credentials.'
        '403':
          description: 'Invalid permissions to perform this action.'
          content:
            application/json: {  }
        '404':
          description: 'Employee not found.'
        '409':
          description: 'The time off request already has a history item.'
          content:
            application/json: {  }
        '503':
          description: 'Service unavailable due to a database error.'
      security:
        -
          basic: []
        -
          oauth:
            - time_off.write
  '/api/v1/employees/{employeeId}/time_off/balance_adjustment':
    put:
      tags:
        - 'Time Off'
        - 'Public API'
      summary: 'Adjust Time Off Balance'
      description: "Creates a balance adjustment for an employee's time off type. The adjustment is recorded as an override history item. Cannot adjust balances for discretionary (unlimited) time off types."
      operationId: adjust-time-off-balance
      parameters:
        -
          name: employeeId
          in: path
          description: 'The ID of the employee.'
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/AdjustTimeOffBalance'
          application/xml:
            schema:
              $ref: '#/components/schemas/AdjustTimeOffBalance'
      responses:
        '201':
          description: 'The balance adjustment has been created.'
          content:
            application/json: {  }
        '400':
          description: 'Empty or malformed JSON/XML, an invalid date format, an invalid time off type, an invalid override amount, or an attempt to adjust a discretionary time off type.'
          content:
            application/json: {  }
        '401':
          description: 'Unauthorized. Invalid API credentials.'
        '403':
          description: 'Insufficient permissions to perform this action.'
          content:
            application/json: {  }
        '404':
          description: 'Employee not found.'
        '503':
          description: 'Service unavailable due to a database error.'
      security:
        -
          basic: []
        -
          oauth:
            - time_off.write
  /api/v1/meta/time_off/policies:
    get:
      tags:
        - 'Time Off'
        - 'Public API'
      summary: 'List Time Off Policies'
      description: 'Returns all non-deleted time off policies for the company, sorted alphabetically by name. Only includes policies whose time off type has not been deleted.'
      operationId: list-time-off-policies
      parameters:
        -
          $ref: '#/components/parameters/AcceptHeaderParameter'
      responses:
        '200':
          description: 'A list of all non-deleted time off policies, sorted alphabetically by name.'
          content:
            application/json:
              schema:
                title: TimeOffPoliciesResponse
                type: array
                items:
                  title: TimeOffPolicy
                  properties: { id: { description: 'The policy ID.', type: integer }, timeOffTypeId: { description: 'The ID of the time off type this policy belongs to.', type: integer }, name: { description: 'The policy name.', type: string }, effectiveDate: { description: 'Deprecated. Always null.', oneOf: [{ type: string }, { type: 'null' }] }, type: { description: 'The policy type.', type: string, enum: [accruing, discretionary, manual] } }
                  type: object
        '401':
          description: 'Unauthorized. Invalid API credentials.'
        '403':
          description: 'Insufficient permissions to view time off policies.'
      security:
        -
          basic: []
        -
          oauth:
            - time_off
  '/api/v1/employees/{employeeId}/time_off/request':
    put:
      tags:
        - 'Time Off'
        - 'Public API'
      summary: 'Create Time Off Request'
      description: "Creates a time off request for an employee. The request can be submitted with a status of `approved`, `denied`, or `requested`. Submitting `approved` or `denied` is only honored when the caller is an owner/admin or has view/edit access to the time off type field for the target employee; other callers receive 403. When honored, these statuses record the request directly and suppress approval notifications. Supplying a `previousRequest` ID performs a destructive supersede: the prior request's status is set to `superceded`, all approvals on its workflow are removed and the workflow is marked deleted, and any home-page notifications tied to that workflow are deleted. Accepts both JSON and XML request bodies."
      operationId: create-time-off-request
      parameters:
        -
          name: employeeId
          in: path
          description: 'The ID of the employee to create the time off request for.'
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TimeOffRequest'
          application/xml:
            schema:
              $ref: '#/components/schemas/TimeOffRequest'
      responses:
        '201':
          description: 'Request created. The `Location` header contains the URL of the new request. When `Accept: application/json` is set, the response body contains the full created request — use the `id` field to chain follow-up operations (e.g. approve, cancel, supersede) without a separate lookup.'
          content:
            application/json:
              schema:
                title: CreatedTimeOffRequest
                properties:
                  id: { description: 'The newly created time off request ID. Use this to chain follow-up operations such as approving, canceling, or superseding the request.', type: integer, example: 1668 }
                  employeeId: { description: 'The ID of the employee the request was created for.', type: integer }
                  name: { description: "The employee's full name. Only present when the employee record could be loaded.", type: string }
                  start: { description: 'The start date of the request in YYYY-MM-DD format.', type: string, format: date }
                  end: { description: 'The end date of the request in YYYY-MM-DD format.', type: string, format: date }
                  created: { description: 'The date the request was created in YYYY-MM-DD format (company timezone).', type: string, format: date }
                  status: { description: 'The current status of the request.', properties: { status: { description: 'The current status value (e.g. `approved`, `denied`, `requested`).', type: string }, lastChanged: { description: 'The date and time the status was last changed, formatted as `YYYY-MM-DD HH:MM:SS` in the company timezone.', type: string }, lastChangedByUserId: { description: 'The user ID who last changed the status.', type: integer } }, type: object }
                  type: { description: 'The time off type for this request. Only present when the underlying time off type record could be loaded.', properties: { id: { description: 'The time off type ID.', type: integer }, name: { description: 'The time off type name.', type: string } }, type: object }
                  amount: { description: 'The amount of time off requested. Only present when the underlying time off type record could be loaded.', properties: { unit: { description: 'The unit of measurement.', type: string, enum: [hours, days] }, amount: { description: 'The total amount requested.', type: number } }, type: object }
                  dates: { description: 'A map of dates (YYYY-MM-DD) to daily amounts. Only present when daily details were attached to the request.', type: object, additionalProperties: { type: number } }
                  notes: { description: 'Notes from employee and/or manager. The object is always present; the `employee` and `manager` keys are each included only when a non-empty note was provided.', properties: { employee: { description: 'Note from the employee.', type: string }, manager: { description: 'Note from the manager.', type: string } }, type: object }
                  comments: { description: 'Comments attached to the request. Always present; empty array when no comments exist.', type: array, items: { type: object } }
                  approvers: { description: 'The approval chain for this request. Only present when the request has approvers.', type: array, items: { $ref: '#/components/schemas/EmployeeTimeOffRequestApproverResponse' } }
                  actions: { description: 'Actions the current user can perform on this request. Only present when PTO action permissions are available. Keys are restricted to `view`, `edit`, `cancel`, `approve`, `deny`, `bypass`; values are booleans.', type: object, additionalProperties: { type: boolean } }
                  overlappingRequests: { description: 'Other existing time off requests whose date ranges overlap this request. Only present when overlap data was loaded with the request.', type: array, items: { type: object } }
                  policyType: { description: 'The policy type backing this request (e.g. accruing, manual, unlimited).', type: string }
                  usedYearToDate: { description: 'Amount of this time off type the employee has used year-to-date as of the request date.', oneOf: [{ type: number }, { type: 'null' }] }
                  balanceOnDateOfRequest: { description: "The employee's balance for this time off type on the date of the request.", oneOf: [{ type: number }, { type: 'null' }] }
                type: object
        '400':
          description: 'Malformed JSON or XML, an invalid time off type, an invalid previous request ID, or other invalid request data.'
          content:
            application/json: {  }
        '401':
          description: 'Unauthorized. Invalid API credentials.'
        '403':
          description: 'Forbidden. The caller does not have permission to create or record this request for the employee, time off type, or requested status.'
          content:
            application/json: {  }
        '404':
          description: 'Employee not found.'
          content:
            application/json: {  }
        '422':
          description: 'Unprocessable entity. Returned when a remote/EOR time off request cannot be created for the requested action.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - time_off.write
  '/api/v1/time_off/requests/{requestId}/status':
    put:
      tags:
        - 'Time Off'
        - 'Public API'
      summary: 'Update Time Off Request Status'
      description: 'Updates the status of an existing time off request. Valid statuses are `approved`, `denied` (or `declined`), and `canceled`. Owner/admins can approve out of turn by completing all workflow steps at once; other approvers complete only their current step.'
      operationId: update-time-off-request-status
      parameters:
        -
          name: requestId
          in: path
          description: 'The ID of the time off request to update.'
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/request'
          application/xml:
            schema:
              $ref: '#/components/schemas/request'
      responses:
        '200':
          description: 'The status has been updated successfully. The response body is intentionally empty — no content is returned. Treat any empty body on a 200 response as a success indicator; do not attempt to parse the body as JSON.'
        '400':
          description: 'If the posted XML is invalid or the status is not "approved", "denied", "canceled", or "declined".'
          content:
            application/json: {  }
        '401':
          description: 'Unauthorized. Invalid API credentials.'
        '403':
          description: 'If the current user doesn\''t have access to change the status in this way.'
          content:
            application/json: {  }
        '404':
          description: 'If the time off request ID doesn\''t exist.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - time_off.write
  /api/v1/time_off/requests:
    get:
      tags:
        - 'Time Off'
        - 'Public API'
      summary: 'List Time Off Requests'
      description: 'Returns time off requests within the specified date range. Both `start` and `end` query parameters are required (YYYY-MM-DD). The search is inclusive: requests whose date range overlaps the query window are returned. Results can be filtered by status, employee, time off type, or limited to requests the caller can approve.'
      operationId: list-time-off-requests
      parameters:
        -
          $ref: '#/components/parameters/AcceptHeaderParameter'
        -
          name: id
          in: query
          description: 'A particular request ID to limit the response to.'
          schema:
            type: integer
        -
          name: action
          in: query
          description: 'Limit to requests the caller can `view`, requests they can `approve`, or only their own requests via `myRequests`. Defaults to `view`.'
          schema:
            type: string
            default: view
            enum:
              - view
              - approve
              - myRequests
        -
          name: employeeId
          in: query
          description: 'A particular employee ID to limit the response to.'
          schema:
            type: integer
        -
          name: start
          in: query
          description: "The left boundary of the search window, in YYYY-MM-DD format. Returns any request whose end date falls on or after this date — i.e., requests that are still active at the start of your window. To find all requests overlapping a date range, pass your range start here. Note: this parameter filters on each request's *end* date, not its start date."
          required: true
          schema:
            type: string
            format: date
        -
          name: end
          in: query
          description: "The right boundary of the search window, in YYYY-MM-DD format. Returns any request whose start date falls on or before this date — i.e., requests that have begun by the end of your window. To find all requests overlapping a date range, pass your range end here. Note: this parameter filters on each request's *start* date, not its end date."
          required: true
          schema:
            type: string
            format: date
        -
          name: type
          in: query
          description: 'A comma-separated list of time off type IDs to filter by. If omitted, requests of all types are included.'
          schema:
            type: string
        -
          name: status
          in: query
          description: 'A comma-separated list of request status values to filter by. Accepted values are approved, denied, superceded, requested, and canceled. If omitted, requests of all statuses are included.'
          schema:
            type: string
        -
          name: excludeNote
          in: query
          description: 'When set to any truthy value, omits the `notes` object from each request in the response.'
          schema:
            type: string
      responses:
        '200':
          description: 'A list of time off requests matching the specified filters.'
          content:
            application/json:
              schema:
                title: TimeOffRequestsResponse
                type: array
                items:
                  title: TimeOffRequest
                  properties: { id: { description: 'The time off request ID.', type: integer, example: 1348 }, employeeId: { description: 'The ID of the employee.', type: integer, example: 5 }, name: { description: "The employee's full name.", type: string }, start: { description: 'The start date of the request in YYYY-MM-DD format.', type: string, format: date }, end: { description: 'The end date of the request in YYYY-MM-DD format.', type: string, format: date }, created: { description: 'The date the request was created in YYYY-MM-DD format (company timezone).', type: string, format: date }, status: { description: 'The current status of the request.', properties: { lastChanged: { description: 'The date the status was last changed (company timezone).', type: string, format: date }, lastChangedByUserId: { description: 'The user ID who last changed the status.', type: integer }, status: { description: 'The current status value.', type: string } }, type: object }, type: { description: 'The time off type for this request.', properties: { id: { description: 'The time off type ID.', type: integer }, name: { description: 'The time off type name.', type: string }, icon: { description: 'The icon name for the time off type.', type: string } }, type: object }, amount: { description: 'The amount of time off requested.', properties: { unit: { description: 'The unit of measurement.', type: string, enum: [hours, days] }, amount: { description: 'The total amount requested.', type: number } }, type: object }, actions: { description: 'Actions the current user can perform on this request.', properties: { view: { description: 'Whether the user can view this request.', type: boolean }, edit: { description: 'Whether the user can edit this request.', type: boolean }, cancel: { description: 'Whether the user can cancel this request.', type: boolean }, approve: { description: 'Whether the user can approve this request.', type: boolean }, deny: { description: 'Whether the user can deny this request.', type: boolean }, bypass: { description: 'Whether the user can bypass the approval workflow.', type: boolean } }, type: object }, dates: { description: 'A map of dates (YYYY-MM-DD) to daily amounts.', type: object, additionalProperties: { type: number } }, notes: { description: 'Notes from employee and/or manager. Omitted when `excludeNote` is set.', properties: { employee: { description: 'Note from the employee.', type: string }, manager: { description: 'Note from the manager.', type: string } }, type: object } }
                  type: object
            application/xml: {  }
        '400':
          description: 'Invalid or missing start/end date.'
          content:
            application/json: {  }
        '401':
          description: 'Unauthorized. Invalid API credentials.'
      security:
        -
          basic: []
        -
          oauth:
            - time_off
  /api/v1/meta/time_off/types:
    get:
      tags:
        - 'Time Off'
        - 'Public API'
      summary: 'List Time Off Types'
      description: "Returns active time off types for the company along with the company's default hours-per-day schedule. Pass `mode=request` to filter to only types the authenticated employee has permission to request."
      operationId: list-time-off-types
      parameters:
        -
          $ref: '#/components/parameters/AcceptHeaderParameter'
        -
          name: mode
          in: query
          description: 'Set to `request` to limit the results to time off types the authenticated employee can request.'
          schema:
            type: string
            enum:
              - request
      responses:
        '200':
          description: "Active time off types and the company's default hours-per-day schedule."
          content:
            application/json:
              schema:
                title: 'Time Off Types and Default Hours'
                required:
                  - timeOffTypes
                  - defaultHours
                properties:
                  timeOffTypes: { title: 'Time Off Types', type: array, items: { required: [id, name, units, color, icon, source], properties: { id: { description: 'Time off type ID', type: integer, example: 64 }, name: { description: 'Time off type name', type: string, example: Vacation }, units: { description: 'Units for time off', type: string, enum: [hours, days] }, color: { description: 'Hex color code', example: 56afdd, oneOf: [{ type: string }, { type: 'null' }] }, icon: { description: 'Icon identifier for the time off type', example: airplane, oneOf: [{ type: string }, { type: 'null' }] }, source: { description: 'Source of the time off type', type: string, enum: [internal, remote] } }, type: object } }
                  defaultHours: { title: 'Default Hours', type: array, items: { required: [name, amount], properties: { name: { description: 'Day of week', type: string, example: Monday }, amount: { description: 'Default hours for the day. Returned as a stringified decimal value.', type: string, example: '8' } }, type: object } }
                type: object
        '401':
          description: 'Unauthorized. Invalid API credentials.'
      security:
        -
          basic: []
        -
          oauth:
            - time_off
  /api/v1/training/type:
    get:
      tags:
        - Training
        - 'Public API'
      summary: 'List Training Types'
      description: 'Returns all training types for the company as an object keyed by training type ID. Each entry includes the training name, renewable status, renewal frequency, required status, due-date window for new hires, category, link URL, description, and self-completion permission. The owner of the API key must have access to training settings.'
      operationId: list-training-types
      responses:
        '200':
          description: 'Returns all training types as an object keyed by training type ID.'
          content:
            application/json:
              schema:
                title: TrainingTypeMap
                description: 'Object keyed by training type ID, where each value is a TrainingType.'
                type: object
                additionalProperties:
                  $ref: '#/components/schemas/TrainingType'
        '400':
          description: 'Bad request parameters.'
          content:
            application/json: {  }
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json: {  }
        '403':
          description: 'Insufficient user permissions or API access is not turned on.'
          content:
            application/json: {  }
        '404':
          description: 'Bad request url.'
          content:
            application/json: {  }
        '500':
          description: 'Internal server error.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - training
    post:
      tags:
        - Training
        - 'Public API'
      summary: 'Create Training Type'
      description: "Creates a new training type. Only 'name' is required; all other fields are optional. When 'renewable' is true, 'frequency' (months between renewals) must also be provided. The 'dueFromHireDate' field is only valid when 'required' is true. The owner of the API key must have access to training settings."
      operationId: create-training-type
      requestBody:
        description: 'Training object to post'
        required: true
        content:
          application/json:
            schema:
              required:
                - name
              properties:
                name:
                  description: 'Name of the new training type.'
                  type: string
                frequency:
                  description: 'The frequency is the (optional) amount of months between renewing trainings. Not valid if training are not renewable.'
                  type: integer
                renewable:
                  description: 'Renewable is optional but if you are setting it to true you must pass a frequency which is the months between renewals.'
                  type: boolean
                category:
                  description: 'The category is optional and you can pass either a category id or a category name.'
                  properties: { id: { description: 'Category ID', type: integer, example: '3' }, name: { description: 'Category Name', type: string, example: 'First Aid Trainings' } }
                  type: object
                required:
                  description: 'Is this a required training?'
                  type: boolean
                dueFromHireDate:
                  description: 'Number of days before the training is due for new hires. Not valid unless training is required.'
                  type: integer
                linkUrl:
                  description: 'Optional URL that can be included with a training.'
                  type: string
                description:
                  description: 'Description for the training.'
                  type: string
                allowEmployeesToMarkComplete:
                  description: 'Allows all employees who can view the training to be able to mark it complete.'
                  type: boolean
              type: object
              example:
                name: 'My New Training'
                frequency: '12'
                renewable: '1'
                category:
                  id: '3'
                  name: CustomApiCategoryName2
                required: ''
                dueFromHireDate:
                  unit: day
                  amount: '30'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TrainingType'
        '400':
          description: 'Bad request parameters.'
          content:
            application/json: {  }
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json: {  }
        '403':
          description: 'Insufficient user permissions or API access is not turned on.'
          content:
            application/json: {  }
        '404':
          description: 'Bad request url.'
          content:
            application/json: {  }
        '500':
          description: 'Internal server error.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - training.write
  '/api/v1/training/type/{trainingTypeId}':
    put:
      tags:
        - Training
        - 'Public API'
      summary: 'Update Training Type'
      description: 'Updates an existing training type. Only provided fields are updated. To remove a category, pass an empty string or null for the category field. Returns 405 when the training type cannot be modified. The owner of the API key must have access to training settings.'
      operationId: update-training-type
      parameters:
        -
          name: trainingTypeId
          in: path
          description: 'The ID of the training type to update.'
          required: true
          schema:
            type: integer
      requestBody:
        description: 'Training type object to update to'
        required: true
        content:
          application/json:
            schema:
              properties:
                name:
                  description: 'Name of the training type.'
                  type: string
                frequency:
                  description: 'The frequency is the (optional) amount of months between renewing trainings. Not valid if training are not renewable.'
                  type: integer
                renewable:
                  description: 'Renewable is optional but if you are setting it to true you must pass a frequency.'
                  type: boolean
                category:
                  description: 'Category is optional and passing an empty value will remove the category from the training type. Passing a name will assign the training type to the new training category.'
                  properties: { id: { description: 'Category ID', type: integer, example: '3' }, name: { description: 'Category Name', type: string, example: CustomApiCategoryName2 } }
                  type: object
                required:
                  description: 'Is this a required training?'
                  type: boolean
                dueFromHireDate:
                  description: 'Number of days before the training is due for new hires. Not valid unless training is required.'
                  type: integer
                linkUrl:
                  description: 'Optional URL that can be included with a training.'
                  type: string
                description:
                  description: 'Description for the training.'
                  type: string
                allowEmployeesToMarkComplete:
                  description: 'Allows all employees who can view the training to be able to mark it complete.'
                  type: boolean
              type: object
              example:
                name: 'My Edited Training'
                frequency: '12'
                renewable: '1'
                category:
                  name: 'Existing or new training category'
                required: ''
                dueFromHireDate:
                  unit: day
                  amount: '30'
      responses:
        '200':
          description: 'Returns the updated training type.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TrainingType'
        '400':
          description: 'Bad request parameters.'
          content:
            application/json: {  }
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json: {  }
        '403':
          description: 'Insufficient user permissions or API access is not turned on.'
          content:
            application/json: {  }
        '404':
          description: 'Bad request url.'
          content:
            application/json: {  }
        '500':
          description: 'Internal server error.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - training.write
    delete:
      tags:
        - Training
        - 'Public API'
      summary: 'Delete Training Type'
      description: 'Delete an existing training type. The owner of the API key must have access to training settings. Deleting a training type will only be successful if all employee trainings for this type have been removed prior to this request.'
      operationId: delete-training-type
      parameters:
        -
          name: trainingTypeId
          in: path
          description: 'The ID of the training type to delete.'
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Deleted successfully. No response body.'
        '400':
          description: 'Bad request. This also occurs when the training type still has associated employee records that must be removed first.'
          content:
            application/json: {  }
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json: {  }
        '403':
          description: 'Insufficient user permissions or API access is not turned on.'
          content:
            application/json: {  }
        '404':
          description: 'Bad request url or training type does not exist.'
          content:
            application/json: {  }
        '405':
          description: 'Training type was unable to be deleted.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - training.write
  /api/v1/training/category:
    get:
      tags:
        - Training
        - 'Public API'
      summary: 'List Training Categories'
      description: 'Returns all training categories for the company as an object keyed by category ID. Each entry contains the category ID and name. The owner of the API key must have access to training settings.'
      operationId: list-training-categories
      responses:
        '200':
          description: 'Returns all training categories as an object keyed by category ID.'
          content:
            application/json:
              schema:
                title: TrainingCategoryMap
                description: 'Object keyed by category ID, where each value is a TrainingCategory.'
                type: object
                additionalProperties:
                  $ref: '#/components/schemas/TrainingCategory'
        '400':
          description: 'Bad request parameters.'
          content:
            application/json: {  }
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json: {  }
        '403':
          description: 'Insufficient user permissions or API access is not turned on.'
          content:
            application/json: {  }
        '404':
          description: 'Bad request url.'
          content:
            application/json: {  }
        '500':
          description: 'Internal server error.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - training
    post:
      tags:
        - Training
        - 'Public API'
      summary: 'Create Training Category'
      description: "Creates a new training category. The 'name' field is required. Returns the created TrainingCategory on success. The owner of the API key must have access to training settings."
      operationId: create-training-category
      requestBody:
        description: 'Training category to post'
        required: true
        content:
          application/json:
            schema:
              required:
                - name
              properties:
                name:
                  description: 'Name of the new training category.'
                  type: string
              type: object
              example:
                name: 'My New Training Category'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TrainingCategory'
        '400':
          description: 'Bad request parameters.'
          content:
            application/json: {  }
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json: {  }
        '403':
          description: 'Insufficient user permissions or API access is not turned on.'
          content:
            application/json: {  }
        '404':
          description: 'Bad request url.'
          content:
            application/json: {  }
        '500':
          description: 'Internal server error.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - training.write
  '/api/v1/training/category/{trainingCategoryId}':
    put:
      tags:
        - Training
        - 'Public API'
      summary: 'Update Training Category'
      description: 'Updates the name of an existing training category. Returns 409 if a category with the same name already exists. The owner of the API key must have access to training settings.'
      operationId: update-training-category
      parameters:
        -
          name: trainingCategoryId
          in: path
          description: 'The ID of the training category to update.'
          required: true
          schema:
            type: integer
      requestBody:
        description: 'Training category to update'
        required: true
        content:
          application/json:
            schema:
              required:
                - name
              properties:
                name:
                  description: 'Name of the training category.'
                  type: string
              type: object
              example:
                name: 'My Training Category'
      responses:
        '200':
          description: 'Returns the updated training category.'
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TrainingCategory'
        '400':
          description: 'Bad request parameters.'
          content:
            application/json: {  }
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json: {  }
        '403':
          description: 'Insufficient user permissions or API access is not turned on.'
          content:
            application/json: {  }
        '404':
          description: 'Bad request url.'
          content:
            application/json: {  }
        '409':
          description: 'A training category with the same name already exists.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - training.write
    delete:
      tags:
        - Training
        - 'Public API'
      summary: 'Delete Training Category'
      description: 'Delete an existing training category. The owner of the API key used must have access to training settings.'
      operationId: delete-training-category
      parameters:
        -
          name: trainingCategoryId
          in: path
          description: 'The ID of the training category to delete.'
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Deleted successfully. No response body.'
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json: {  }
        '403':
          description: 'Insufficient user permissions or API access is not turned on.'
          content:
            application/json: {  }
        '404':
          description: 'Bad request url or training category does not exist.'
          content:
            application/json: {  }
        '500':
          description: 'Internal server error.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - training.write
  '/api/v1/training/record/employee/{employeeId}':
    get:
      tags:
        - Training
        - 'Public API'
      summary: 'List Employee Training Records'
      description: "Returns all training records for the specified employee as an object keyed by training record ID. Use the optional 'type' query parameter to filter by training type ID. Fields such as instructor, credits, hours, and cost are only included when enabled in the company's training settings. The owner of the API key must have permission to view the employee."
      operationId: list-employee-trainings
      parameters:
        -
          name: employeeId
          in: path
          description: 'The ID of the employee to get a list of trainings for.'
          required: true
          schema:
            type: integer
        -
          name: type
          in: query
          description: 'Optional training type ID to filter records. Omitting this parameter returns all training records for the employee.'
          schema:
            type: integer
      responses:
        '200':
          description: 'Returns training records keyed by record ID, or an empty array when the employee has no records.'
          content:
            application/json:
              schema:
                title: TrainingRecordMap
                description: 'Returns an empty array when the employee has no records, or an object keyed by training record ID when records exist.'
                oneOf:
                  - { description: 'Object keyed by training record ID, each value is a TrainingRecord.', type: object, additionalProperties: { $ref: '#/components/schemas/TrainingRecord' } }
                  - { description: 'Empty array returned when the employee has no training records.', type: array, items: {  }, maxItems: 0 }
        '400':
          description: 'Bad request parameters.'
          content:
            application/json: {  }
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json: {  }
        '403':
          description: 'Insufficient user permissions or API access is not turned on.'
          content:
            application/json: {  }
        '404':
          description: 'Bad request url.'
          content:
            application/json: {  }
        '500':
          description: 'Internal server error.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - training
    post:
      tags:
        - Training
        - 'Public API'
      summary: 'Create Employee Training Record'
      description: "Creates a new training record for the specified employee. The 'completed' date (yyyy-mm-dd) and 'type' (training type ID) are required. Optional fields include instructor, hours, credits, notes, and cost. The owner of the API key must have permission to add trainings for the employee."
      operationId: create-employee-training-record
      parameters:
        -
          name: employeeId
          in: path
          description: 'The ID of the employee to add a training record to.'
          required: true
          schema:
            type: integer
      requestBody:
        description: 'Training object to post'
        required: true
        content:
          application/json:
            schema:
              required:
                - completed
                - type
              properties:
                completed:
                  description: 'Completed is a required field and must be in yyyy-mm-dd format.'
                  type: string
                cost:
                  description: 'Optional cost for the training record.'
                  properties: { currency: { description: "ISO 4217 currency code (e.g. 'USD').", type: string }, amount: { description: "Monetary amount as a decimal string (e.g. '100.00').", type: string } }
                  type: object
                instructor:
                  description: 'Name of the training instructor.'
                  type: string
                hours:
                  description: 'Number of hours for the training.'
                  type: number
                credits:
                  description: 'Credits earned for the training.'
                  type: number
                notes:
                  description: 'Optional notes about the training record.'
                  type: string
                type:
                  description: 'This must be an existing training type ID.'
                  type: integer
              type: object
              example:
                completed: '2026-03-12'
                cost:
                  currency: USD
                  amount: '100.00'
                instructor: 'Bob Jones'
                hours: '16'
                credits: '4'
                notes: 'sample note'
                type: '4'
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TrainingRecord'
        '400':
          description: 'Bad request parameters.'
          content:
            application/json: {  }
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json: {  }
        '403':
          description: 'Insufficient user permissions or API access is not turned on.'
          content:
            application/json: {  }
        '404':
          description: 'Bad request url.'
          content:
            application/json: {  }
        '500':
          description: 'Internal server error.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - training.write
  '/api/v1/training/record/{employeeTrainingRecordId}':
    put:
      tags:
        - Training
        - 'Public API'
      summary: 'Update Employee Training Record'
      description: "Updates an existing employee training record. The 'completed' date (yyyy-mm-dd) is required; all other fields are optional. Returns the updated TrainingRecord with HTTP 201. Returns 405 when the record cannot be updated. The owner of the API key must have permission to edit trainings for the employee."
      operationId: update-employee-training-record
      parameters:
        -
          name: employeeTrainingRecordId
          in: path
          description: 'The ID of the training record to update.'
          required: true
          schema:
            type: integer
      requestBody:
        description: 'Training object to update'
        required: true
        content:
          application/json:
            schema:
              required:
                - completed
              properties:
                completed:
                  description: 'Completed is the only required field and must be in yyyy-mm-dd format. The other parameters are optional.'
                  type: string
                cost:
                  description: 'Optional cost for the training record.'
                  properties: { currency: { description: "ISO 4217 currency code (e.g. 'USD').", type: string }, amount: { description: "Monetary amount as a decimal string (e.g. '100.00').", type: string } }
                  type: object
                instructor:
                  description: 'Name of the training instructor.'
                  type: string
                hours:
                  description: 'Number of hours for the training.'
                  type: number
                credits:
                  description: 'Credits earned for the training.'
                  type: number
                notes:
                  description: 'Optional notes about the training record.'
                  type: string
              type: object
              example:
                completed: '2026-03-12'
                cost:
                  currency: USD
                  amount: '100.00'
                instructor: 'Bob Jones'
                hours: '16'
                credits: '4'
                notes: 'sample note'
      responses:
        '201':
          description: Updated
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TrainingRecord'
        '400':
          description: 'Bad request parameters.'
          content:
            application/json: {  }
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json: {  }
        '403':
          description: 'Insufficient user permissions or API access is not turned on.'
          content:
            application/json: {  }
        '404':
          description: 'Bad request url.'
          content:
            application/json: {  }
        '405':
          description: 'Training record could not be updated.'
          content:
            application/json: {  }
        '500':
          description: 'Internal server error.'
      security:
        -
          basic: []
        -
          oauth:
            - training.write
    delete:
      tags:
        - Training
        - 'Public API'
      summary: 'Delete Employee Training Record'
      description: 'Delete an existing employee training record. The owner of the API key used must have permission to view and edit the employee and training type.'
      operationId: delete-employee-training-record
      parameters:
        -
          name: employeeTrainingRecordId
          in: path
          description: 'The ID of the training record to delete.'
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: 'Deleted successfully. No response body.'
        '401':
          description: 'Unauthorized. Invalid API credentials.'
          content:
            application/json: {  }
        '403':
          description: 'Insufficient user permissions or API access is not turned on.'
          content:
            application/json: {  }
        '404':
          description: 'Bad request url or training record does not exist.'
          content:
            application/json: {  }
        '405':
          description: 'Unable to delete training record.'
          content:
            application/json: {  }
      security:
        -
          basic: []
        -
          oauth:
            - training.write
  '/api/v1/employees/{employeeId}/photo':
    post:
      tags:
        - Photos
        - 'Public API'
      summary: 'Upload Employee Photo'
      description: "Uploads a new photo for an employee. Accepts either a `multipart/form-data` POST with a `file` field carrying raw binary image bytes (typical browser/SDK usage), or an `application/json` POST with a `fileBase64` property carrying the base64-encoded image bytes (typical MCP/LLM-connector usage). The server selects the path based on the request `Content-Type`. Supported formats: JPEG, PNG, BMP, GIF. Other formats (HEIC, SVG, AVIF, WebP) are rejected with 415; TIFF is accepted by the format gate but some variants may fail downstream. The image must be square (width and height must match within one pixel) and at least 150×150 pixels. Maximum file size is 20MB (applies to the decoded bytes for the JSON variant). The photo replaces the employee's current photo for all size variants. Employees may upload their own photo if the company has self-photo uploads enabled."
      operationId: upload-employee-photo
      parameters:
        -
          name: employeeId
          in: path
          description: 'The ID of the employee whose photo is being uploaded.'
          required: true
          schema:
            type: integer
      requestBody:
        required: true
        content:
          multipart/form-data:
            schema:
              required:
                - file
              properties:
                file:
                  description: "The image file to upload as the employee's photo. Supported formats: JPEG, PNG, BMP, GIF (HEIC, SVG, AVIF, and WebP are rejected with 415; TIFF is accepted by the format gate but some variants may fail downstream). Must be square (width and height within 1 pixel), at least 150×150 pixels, and no larger than 20MB."
                  type: string
                  format: binary
              type: object
          application/json:
            schema:
              required:
                - fileBase64
              properties:
                fileBase64:
                  description: 'Base64-encoded image bytes. Same format/size/dimension rules as the multipart `file` field: JPEG, PNG, BMP, or GIF (HEIC, SVG, AVIF, and WebP are rejected with 415; TIFF is accepted by the format gate but some variants may fail downstream); square within 1 pixel; at least 150×150 pixels; decoded payload no larger than 20MB. Whitespace inside the base64 string is tolerated and stripped before decoding.'
                  type: string
                  format: byte
              type: object
      responses:
        '201':
          description: 'The photo was uploaded and processed successfully. No response body is returned.'
        '400':
          description: 'The request is invalid: no file provided, zero-byte file, or the maximum number of photo uploads (32767) has been exceeded.'
        '402':
          description: 'The photo could not be processed: the image crop failed, or the file contents could not be read.'
        '403':
          description: 'The authenticated user does not have permission to upload photos for this employee.'
        '404':
          description: 'The employee does not exist.'
        '413':
          description: 'The uploaded file exceeds the 20MB size limit.'
        '415':
          description: 'The image does not meet requirements: not square (width and height differ by more than one pixel), smaller than 150×150 pixels, or the file could not be read as a supported image format.'
      security:
        -
          basic: []
        -
          oauth:
            - 'employee:photo.write'
  /api/v1/time_off/whos_out:
    get:
      tags:
        - 'Time Off'
        - 'Public API'
      summary: 'List Who’s Out'
      description: 'Returns a date-sorted list of employees who are out and company holidays for the specified period. Defaults to today through 14 days out when dates are omitted. Results include both `timeOff` entries (employee requests) and `holiday` entries, each identified by `type`. An empty array may mean no one is out, or that no holidays have been configured in the BambooHR company calendar — holidays must be set up there before they appear here. The `filter: off` parameter applies only to employee time-off entries; holidays are independently filtered per-employee based on holiday visibility settings, and that filter is not disabled by `filter: off`.'
      operationId: list-whos-out
      parameters:
        -
          $ref: '#/components/parameters/AcceptHeaderParameter'
        -
          name: start
          in: query
          description: 'Start date in YYYY-MM-DD format. Defaults to today.'
          schema:
            type: string
            format: date
        -
          name: end
          in: query
          description: 'End date in YYYY-MM-DD format. Defaults to 14 days after the start date.'
          schema:
            type: string
            format: date
        -
          name: filter
          in: query
          description: "Controls the Who's Out calendar filter. By default (parameter omitted), results are limited to the set of employees defined by the authenticated user's saved Who's Out calendar filter (the same filter applied to their in-app Who's Out view). A user with no filter configured sees all employees; a user with a saved filter (e.g. by department, location, division) sees only the configured subset. Set to `off` to ignore the saved filter and return employee time-off entries for everyone — useful for admins or integrations that need the complete company-wide view, or to diagnose whether incomplete results are caused by the saved filter. Note: this parameter applies to employee `timeOff` entries only. `holiday` entries are filtered separately on a per-employee basis (holidays can be configured as visible to specific employees) and that filter is not affected by `filter: off`."
          schema:
            type: string
            enum:
              - 'off'
      responses:
        '200':
          description: 'A date-sorted list of employees who are out and company holidays.'
          content:
            application/json:
              schema:
                title: WhosOutResponse
                type: array
                items:
                  title: WhosOutEntry
                  properties: { id: { description: 'The time off request ID or holiday ID.', type: integer }, type: { description: "Whether this entry is a time off request or a company holiday. Entries have two shapes depending on `type`: `timeOff` entries include `employeeId` and `name` (the employee's name); `holiday` entries include only `name` (the holiday name) and omit `employeeId`. Always check `type` before accessing `employeeId`.", type: string, enum: [timeOff, holiday] }, employeeId: { description: 'The employee ID. Only present for `timeOff` entries; omitted for `holiday` entries.', type: integer }, name: { description: "For `timeOff` entries, the employee's name. For `holiday` entries, the holiday's name.", type: string }, start: { description: 'Start date in YYYY-MM-DD format.', type: string, format: date }, end: { description: 'End date in YYYY-MM-DD format.', type: string, format: date } }
                  type: object
            application/xml: {  }
        '401':
          description: 'Unauthorized. Invalid API credentials.'
        '403':
          description: "The Who's Out feature is disabled for this account."
      security:
        -
          basic: []
        -
          oauth:
            - time_off
components:
  schemas:
    CursorPagedResponseMetadata:
      description: 'Metadata information for employee list responses including total count and pagination details'
      required:
        - total
        - page
      properties:
        total:
          description: 'Total number of employees matching the filter criteria'
          type: integer
        page:
          $ref: '#/components/schemas/CursorPagesResponse'
          description: 'Pagination information for the current response'
      type: object
    CursorPagesResponse:
      description: 'Pagination information for employee list responses including limit and cursor-based navigation'
      required:
        - limit
        - nextCursor
        - prevCursor
      properties:
        limit:
          description: 'Maximum number of items per page'
          oneOf:
            -
              type: integer
            -
              type: 'null'
        nextCursor:
          description: 'Cursor for the next page of results, null if no more pages. This should be used with the after pagination parameter.'
          oneOf:
            -
              type: string
            -
              type: 'null'
        prevCursor:
          description: 'Cursor for the previous page of results, null if on first page. This should be used with the before pagination parameter.'
          oneOf:
            -
              type: string
            -
              type: 'null'
      type: object
    CursorPaginationQueryObject:
      properties:
        before:
          description: 'Cursor pointing to the start of the previous page. Use the `prevCursor` value from the last response to paginate backward.'
          type: string
        after:
          description: 'Cursor pointing to the start of the next page. Use the `nextCursor` value from the last response to paginate forward.'
          type: string
        limit:
          description: 'Maximum number of items to return. This can be at most 100.'
          type: integer
          default: 50
          maximum: 100
          minimum: 1
      type: object
    EmployeeVerificationIntegration:
      title: EmployeeVerificationIntegration
      description: "Install state and partner metadata for the company's employee verification integration."
      required:
        - enabled
        - integrationType
      properties:
        enabled:
          description: 'True when the integration is installed for the company and partner endpoints are usable.'
          type: boolean
        integrationType:
          description: 'Identifier of the configured employee verification partner (opaque string; matches `integrationType` from the integration GET response).'
          type: string
      type: object
    EmployeeVerificationPublicApiRecord:
      title: EmployeeVerificationPublicApiRecord
      description: 'A single employee verification row as returned by the public list API.'
      properties:
        id:
          type: integer
          format: int64
        employeeId:
          oneOf:
            -
              type: integer
              format: int64
            -
              type: 'null'
        integrationType:
          type: string
        verificationType:
          type: string
        verificationStatus:
          oneOf:
            -
              type: string
            -
              type: 'null'
        verificationStatusNotes:
          oneOf:
            -
              type: string
            -
              type: 'null'
        remoteAccessUrl:
          oneOf:
            -
              type: string
            -
              type: 'null'
        eVerifyStatus:
          oneOf:
            -
              type: string
            -
              type: 'null'
        archived:
          type: boolean
        billingProcessed:
          type: boolean
        createdByUserId:
          type: integer
          format: int64
        createdYmdt:
          description: 'Creation timestamp (company DB format).'
          type: string
        lastModifiedYmdt:
          description: 'Last modified timestamp (company DB format).'
          type: string
        startDateYmdt:
          type: string
      type: object
    NewHireWidgetItem:
      description: "Single upcoming new hire as projected for the public welcome-new-hires widget endpoint. Strips sensitive contact fields (work email, home email) and normalizes the domain's dynamic BasicEmployee decorations into a stable shape."
      required:
        - id
        - preferredFirstName
        - lastName
        - hireDate
        - canSeeEmployee
      properties:
        id:
          description: 'Employee id.'
          type: integer
        preferredFirstName:
          description: 'Nickname when set, otherwise first name.'
          type: string
        lastName:
          type: string
        hireDate:
          description: 'ISO 8601 date (YYYY-MM-DD).'
          oneOf:
            -
              type: string
              format: date
            -
              type: 'null'
        profilePictureUrl:
          description: 'Medium-size profile photo URL, or default silhouette URL.'
          oneOf:
            -
              type: string
            -
              type: 'null'
        department:
          description: 'Department display name. Null when the authenticated user cannot view the department field.'
          oneOf:
            -
              type: string
            -
              type: 'null'
        location:
          description: 'Location display name. Null when the authenticated user cannot view the location field.'
          oneOf:
            -
              type: string
            -
              type: 'null'
        canSeeEmployee:
          description: "Whether the authenticated user can navigate to the employee's profile."
          type: boolean
        getToKnowYou:
          description: 'Completed Get-To-Know-You questions and responses for the new hire. Null when the new hire has not completed any questions or any answer is hidden.'
          oneOf:
            -
              type: array
              items:
                $ref: '#/components/schemas/CompletedQuestionsAndResponseDataObject'
            -
              type: 'null'
      type: object
    NewHirePacketPublicApi:
      description: 'Flat new hire packet instance for public read APIs (list and detail).'
      properties:
        id:
          description: 'Primary key of the new hire packet instance.'
          oneOf:
            -
              type: integer
            -
              type: 'null'
        employeeId:
          description: 'Employee this packet belongs to.'
          type: integer
        contactEmployeeId:
          description: 'Contact employee for the packet, if any.'
          oneOf:
            -
              type: integer
            -
              type: 'null'
        requiresPersonalInformation:
          type: boolean
        requiresPhoto:
          type: boolean
        requiresPersonalQuestions:
          type: boolean
        arriveByTime:
          description: 'Expected arrival time (time portion).'
          oneOf:
            -
              type: string
            -
              type: 'null'
        location:
          description: 'Work location label for the new hire.'
          oneOf:
            -
              type: string
            -
              type: 'null'
        sentDatetime:
          description: 'When the packet was sent (datetime string).'
          oneOf:
            -
              type: string
            -
              type: 'null'
        viewedDatetime:
          description: 'When the packet was first viewed (datetime string).'
          oneOf:
            -
              type: string
            -
              type: 'null'
        completedDatetime:
          description: 'When the packet was completed (datetime string).'
          oneOf:
            -
              type: string
            -
              type: 'null'
        createdByUserId:
          oneOf:
            -
              type: integer
            -
              type: 'null'
        createdDatetime:
          description: 'When the packet row was created (datetime string).'
          oneOf:
            -
              type: string
            -
              type: 'null'
        sendGetToKnowYouEmail:
          type: boolean
        getToKnowYouEmailSent:
          oneOf:
            -
              type: boolean
            -
              type: 'null'
        showPayrollState:
          oneOf:
            -
              type: boolean
            -
              type: 'null'
        showPayrollFederal:
          oneOf:
            -
              type: boolean
            -
              type: 'null'
        showPayrollDirectDeposit:
          oneOf:
            -
              type: boolean
            -
              type: 'null'
        nhpConfigurationId:
          description: 'Linked new hire packet configuration id, if any.'
          oneOf:
            -
              type: integer
            -
              type: 'null'
        nhpTemplateId:
          oneOf:
            -
              type: integer
            -
              type: 'null'
        countNhpGtkySent:
          oneOf:
            -
              type: integer
            -
              type: 'null'
        cancelled:
          type: boolean
        includePhotoOption:
          description: 'Whether photo collection is offered based on configuration.'
          type: boolean
        status:
          description: 'Derived lifecycle status for the instance.'
          type: string
          enum:
            - cancelled
            - completed
            - sent
            - draft
      type: object
    NewHirePacketPublicApiWritableBody:
      description: 'Optional fields that may be set when creating or updating a new hire packet instance. All properties are optional for PUT; for POST, employeeId is required and supplied via a second schema in the request body allOf.'
      properties:
        contactEmployeeId:
          description: 'Contact employee for the packet, if any.'
          oneOf:
            -
              type: integer
            -
              type: 'null'
        requiresPersonalInformation:
          description: 'Whether personal information collection is required.'
          type: boolean
        requiresPhoto:
          description: 'Whether a photo is required.'
          type: boolean
        requiresPersonalQuestions:
          description: 'Whether personal questions are required.'
          type: boolean
        arriveByTime:
          description: 'Expected arrival time (time portion).'
          oneOf:
            -
              type: string
            -
              type: 'null'
        location:
          description: 'Work location label for the new hire.'
          oneOf:
            -
              type: string
            -
              type: 'null'
        sendGetToKnowYouEmail:
          description: 'Whether to send the Get To Know You email.'
          type: boolean
        showPayrollState:
          description: 'Whether to show state payroll fields.'
          type: boolean
        showPayrollFederal:
          description: 'Whether to show federal payroll fields.'
          type: boolean
        showPayrollDirectDeposit:
          description: 'Whether to show direct deposit payroll fields.'
          type: boolean
        nhpTemplateId:
          description: 'Linked new hire packet template id, if any.'
          oneOf:
            -
              type: integer
            -
              type: 'null'
      type: object
    OnboardingExperiencePublicApi:
      description: 'Onboarding experience for public APIs; links to the new hire packet instance by id only.'
      properties:
        id:
          description: 'Onboarding experience id (same as the new hire packet instance primary key).'
          type: integer
        employeeId:
          description: 'Employee this onboarding experience belongs to.'
          type: integer
        newHirePacketId:
          description: 'Primary key of the linked new hire packet instance (NHP row).'
          type: integer
        nhpTemplateId:
          description: 'Linked NHP template id, if any.'
          oneOf:
            -
              type: integer
            -
              type: 'null'
        nhpConfigurationId:
          description: 'Linked NHP configuration id, if any.'
          oneOf:
            -
              type: integer
            -
              type: 'null'
        status:
          description: 'Derived lifecycle status for the linked new hire packet instance.'
          type: string
          enum:
            - cancelled
            - completed
            - sent
            - draft
      type: object
    ProjectCreateRequestSchema:
      description: 'Schema for creating a new time tracking project with optional tasks'
      required:
        - name
      properties:
        name:
          description: 'Name of the project. Must be unique and no more than 50 characters.'
          type: string
          maxLength: 50
          example: 'Project A'
        billable:
          description: 'Indicates if the project is billable. Defaults to true if not provided.'
          type: boolean
        allowAllEmployees:
          description: 'Indicates if all employees can log time for this project. Defaults to true if not provided.'
          type: boolean
        employeeIds:
          description: 'A list of employee IDs that can log time for this project. Only used when `allowAllEmployees` is false.'
          type: array
          items:
            type: integer
        hasTasks:
          description: 'Indicates if the project has tasks. Defaults to false if not provided.'
          type: boolean
        tasks:
          description: 'List of tasks to create and associate with the project.'
          type: array
          items:
            $ref: '#/components/schemas/TaskCreateSchema'
      type: object
    TaskCreateSchema:
      description: 'Schema for creating a new task for a time tracking project'
      required:
        - name
      properties:
        name:
          description: 'Name of the task.'
          type: string
        billable:
          description: 'Indicates if the task is billable. Defaults to true if not provided.'
          type: boolean
      type: object
    Scheduling-CreateSchedulingShiftRequest-V1:
      description: 'Fields required to create a scheduling shift.'
      required:
        - scheduleId
        - status
        - color
        - timezone
        - start
        - end
      properties:
        scheduleId:
          description: 'The ID of the schedule the shift belongs to.'
          type: string
          format: uuid
          example: 0199de9b-6bcb-77e7-941d-3caf74b9a372
        name:
          description: 'The name of the shift.'
          example: 'Morning Shift'
          oneOf:
            -
              type: string
            -
              type: 'null'
        status:
          description: 'The status of the shift.'
          enum:
            - planned
            - published
        color:
          description: '6 character color hex code.'
          type: string
          maxLength: 6
          minLength: 6
          example: 00FFAA
        timezone:
          description: 'The timezone for the shift. Defaults to schedule timezone.'
          example: America/Denver
        capacity:
          description: 'The expected headcount for the shift. If null, has no restrictions.'
          default: null
          example: 1
          oneOf:
            -
              type: integer
              minimum: 1
            -
              type: 'null'
        employeeIds:
          description: 'What employees are assigned to this shift?'
          type: array
          items:
            type: integer
          default: []
        start:
          description: 'UTC timestamp of the start of the shift.'
          type: string
          format: date-time
          example: '2025-01-01T06:00:00Z'
        end:
          description: 'UTC timestamp of the end of the shift.'
          type: string
          format: date-time
          example: '2025-01-01T14:00:00Z'
        recurrenceRule:
          description: 'The recurrence rule. Follows the iCalendar RFC 5545 RRULE syntax.'
          example: FREQ=WEEKLY;INTERVAL=1
          oneOf:
            -
              type: string
            -
              type: 'null'
        recurrenceDtstart:
          description: 'The UTC start time for a recurring shift (anchor point for the recurrence rule).'
          example: '2024-01-01T09:00:00Z'
          oneOf:
            -
              type: string
            -
              type: 'null'
        recurrenceDtend:
          description: 'The UTC end time for a recurring shift.'
          example: '2024-12-01T17:00:00Z'
          oneOf:
            -
              type: string
            -
              type: 'null'
        recurrenceUntil:
          description: 'The UTC datetime when the recurrence series stops generating new occurrences (UNTIL in RRULE).'
          example: '2024-12-31T23:59:59Z'
          oneOf:
            -
              type: string
            -
              type: 'null'
      type: object
    Scheduling-UpdateSchedulingShiftRequest-V1:
      description: 'Fields for updating a scheduling shift. All fields are optional, and only provided fields will be updated. recurrenceEditOption is required when the shift already has recurrence settings.'
      properties:
        name:
          description: 'The name of the shift'
          example: 'Front Desk - Morning'
          oneOf:
            -
              type: string
            -
              type: 'null'
        color:
          description: '6 character color hex code.'
          type: string
          maxLength: 6
          minLength: 6
          example: 00FFAA
        capacity:
          description: 'The expected headcount for the shift. If null, has no restrictions.'
          default: null
          example: 3
          oneOf:
            -
              type: integer
              minimum: 1
            -
              type: 'null'
        start:
          description: 'UTC timestamp of the start of the shift.'
          type: string
          format: date-time
          example: '2026-01-14T16:00:00Z'
        end:
          description: 'UTC timestamp of the end of the shift.'
          type: string
          format: date-time
          example: '2026-01-14T20:00:00Z'
        timezone:
          description: 'The timezone for the shift.'
          type: string
          example: America/Denver
        recurrenceRule:
          description: 'The recurrence rule. Follows the iCalendar RFC 5545 RRULE syntax.'
          example: 'FREQ=WEEKLY;BYDAY=MO,WE;INTERVAL=1'
          oneOf:
            -
              type: string
            -
              type: 'null'
        recurrenceEditOption:
          description: 'How should recurrence edits be effective? Edits will not affect any realized shifts regardless of option.'
          type: string
          enum:
            - instance
            - future
            - all
          example: future
        recurrenceDtstart:
          description: 'The UTC start time for a recurring shift and the anchor point for the recurrence rule.'
          example: '2026-01-14T09:00:00Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        recurrenceDtend:
          description: 'The UTC end time for a recurring shift.'
          example: '2026-01-14T17:00:00Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        recurrenceUntil:
          description: 'The UTC datetime when the recurrence series stops generating new occurrences (UNTIL in RRULE).'
          example: '2026-03-31T17:00:00Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        employeeIds:
          description: 'What employees are assigned to this shift?'
          type: array
          items:
            type: integer
        unpublishedChanges:
          description: 'The pending changes that have not been published. Only accepts null to clear unpublished changes.'
          type: 'null'
      type: object
    EmployeeTimeOffRequestApproverResponse:
      description: 'List of time off request approvers'
      type: array
      items:
        required:
          - userId
          - displayName
          - employeeId
        properties:
          userId:
            description: 'User ID of the approver'
            type: integer
          displayName:
            description: 'Display name of the approver'
            type: string
          employeeId:
            description: 'Employee ID of the approver'
            type: integer
          photoUrl:
            description: "URL to the approver's photo"
            oneOf:
              -
                type: string
              -
                type: 'null'
        type: object
    PersonInfoApiTransformer:
      properties:
        employeeId:
          description: 'Employee ID'
          oneOf:
            -
              type: integer
            -
              type: 'null'
        userId:
          description: 'User ID'
          oneOf:
            -
              type: integer
            -
              type: 'null'
        displayFirstName:
          description: 'Display first name'
          type: string
        lastName:
          description: 'Last name'
          type: string
        photoUrl:
          description: 'Photo URL'
          type: string
      type: object
    TimeTracking-OffsetPaginatedResponseData-V1:
      description: 'Additional data for paginated responses'
      properties:
        meta:
          description: 'Pagination metadata'
          required:
            - totalItems
            - offset
            - limit
          properties:
            totalItems:
              type: integer
              example: 3
            offset:
              type: integer
              example: 2
            limit:
              type: integer
              example: 2
          type: object
        _links:
          description: 'Pagination links'
          required:
            - prev
            - next
          properties:
            prev:
              description: 'Link object for the previous page, or null if on the first page'
              properties:
                href:
                  description: 'Full URL for the previous page'
                  type: string
              oneOf:
                -
                  type: object
                -
                  type: 'null'
            next:
              description: 'Link object for the next page, or null if on the last page'
              properties:
                href:
                  description: 'Full URL for the next page'
                  type: string
              oneOf:
                -
                  type: object
                -
                  type: 'null'
          type: object
      type: object
    TimeTracking-BreakPolicyEmployeeCollection-V1:
      description: 'Employee data relevant for break policies'
      type: array
      items:
        required:
          - id
          - name
          - photoUrl
        properties:
          id:
            description: 'The employee ID'
            type: integer
          name:
            description: 'The employee name {Preferred Last}'
            oneOf:
              -
                type: string
              -
                type: 'null'
          photoUrl:
            description: 'The employee profile photo'
            oneOf:
              -
                type: string
              -
                type: 'null'
        type: object
    Project-PaginatedResponseData-V1:
      description: 'Pagination envelope shared by paginated Project endpoints.'
      properties:
        meta:
          description: 'Pagination metadata.'
          required:
            - totalItems
            - totalPages
            - page
            - pageSize
          properties:
            totalItems:
              type: integer
              example: 100
            totalPages:
              type: integer
              example: 2
            page:
              type: integer
              example: 1
            pageSize:
              type: integer
              example: 50
          type: object
        _links:
          description: 'Pagination links. Keys are omitted when there is no previous/next page.'
          properties:
            prev:
              description: 'Link object for the previous page. Omitted on the first page.'
              properties:
                href:
                  type: string
              type: object
            next:
              description: 'Link object for the next page. Omitted on the last page.'
              properties:
                href:
                  type: string
              type: object
          type: object
      type: object
    Project-CreateTimeTrackingProject-V1:
      description: 'Request body for creating a time tracking project.'
      required:
        - name
      properties:
        name:
          type: string
          maxLength: 50
          minLength: 1
          example: 'Project A'
        billable:
          type: boolean
          default: false
          example: true
        includeInPayroll:
          type: boolean
          default: false
          example: true
        allEmployeesAssigned:
          description: 'If true, every time-tracked employee is assigned to the project. Ignored when `employeeIds` is provided.'
          type: boolean
          default: false
        employeeIds:
          description: 'Specific employee IDs to assign. Minimum 1 entry required when provided. Takes precedence over `allEmployeesAssigned`.'
          type: array
          items:
            type: integer
          minItems: 1
          example:
            - 1
            - 2
            - 3
        tasks:
          description: 'Tasks to create alongside the project. Minimum 1 entry required when provided.'
          type: array
          items:
            required:
              - name
            properties:
              name:
                type: string
                maxLength: 50
                minLength: 1
                example: 'Task A'
              billable:
                type: boolean
                default: false
                example: true
            type: object
          minItems: 1
      type: object
    AdjustTimeTrackingRequestSchema:
      description: 'Schema for adjust time tracking request body'
      required:
        - timeTrackingId
        - hoursWorked
      properties:
        timeTrackingId:
          description: 'The time tracking id is the id that was used to track the record up to 36 characters in length. (i.e. UUID).'
          type: string
          example: 550e8400-e29b-41d4-a716-446655440000
        hoursWorked:
          description: 'The updated number of hours worked. e.g. if Employee A worked 8.0 hours originally and decided they only worked 6.0, please send 6.0 here not -2.0.'
          type: number
          example: 6
        projectId:
          description: 'ID of the project associated with this time tracking record'
          example: 123
          oneOf:
            -
              type: integer
            -
              type: 'null'
        taskId:
          description: 'ID of the task associated with this time tracking record'
          example: 456
          oneOf:
            -
              type: integer
            -
              type: 'null'
        shiftDifferentialId:
          description: 'ID of the shift differential associated with this time tracking record'
          example: 789
          oneOf:
            -
              type: integer
            -
              type: 'null'
        holidayId:
          description: 'ID of the holiday associated with this time tracking record'
          example: 101
          oneOf:
            -
              type: integer
            -
              type: 'null'
      type: object
    ClockInRequestSchema:
      description: 'Schema for clock-in request body'
      properties:
        projectId:
          description: 'ID of the time tracking project that should be associated with the timesheet entry. Required if taskId is specified.'
          example: 10
          oneOf:
            -
              type: integer
            -
              type: 'null'
        taskId:
          description: 'ID of the time tracking task that should be associated with the timesheet entry.'
          example: 25
          oneOf:
            -
              type: integer
            -
              type: 'null'
        note:
          description: 'The note that should be associated with the timesheet entry'
          example: 'Back from lunch.'
          oneOf:
            -
              type: string
            -
              type: 'null'
        date:
          description: 'Date for the timesheet entry. Must be in YYYY-MM-DD format.'
          example: '2024-12-12'
          oneOf:
            -
              type: string
              format: date
            -
              type: 'null'
        start:
          description: 'The time for the clock in. In 24 hour format HH:MM'
          example: '13:00'
          oneOf:
            -
              type: string
              pattern: '^([01]?[0-9]|2[0-3]):[0-5][0-9]$'
            -
              type: 'null'
        timezone:
          description: 'The timezone associated with the clock in.'
          example: America/Denver
          oneOf:
            -
              type: string
            -
              type: 'null'
        breakId:
          description: 'ID of the break that should be associated with the timesheet entry.'
          example: 123e4567-e89b-12d3-a456-426614174000
          oneOf:
            -
              type: string
              format: uuid
            -
              type: 'null'
        offline:
          description: 'Whether this is an offline punch. When true, bypasses the shift schedule clock-in restriction. Intended for devices that store punches offline and sync later.'
          type: boolean
          example: false
      type: object
    ClockOutRequestSchema:
      description: 'Schema for clock out request body'
      properties:
        date:
          description: 'Date for the timesheet entry. Must be in YYYY-MM-DD format.'
          type: string
          format: date
          example: '2024-12-12'
        end:
          description: 'The time for the clock out. In 24 hour format HH:MM'
          type: string
          pattern: '^([01]?[0-9]|2[0-3]):[0-5][0-9]$'
          example: '13:00'
        timezone:
          description: 'The timezone associated with the clock out.'
          type: string
          example: America/Denver
      type: object
    HourEntriesRequestSchema:
      description: 'Schema for timesheet hour entries request'
      required:
        - hours
      properties:
        hours:
          description: 'Array of hour entries to add or update'
          type: array
          items:
            $ref: '#/components/schemas/HourEntrySchema'
      type: object
    HourEntrySchema:
      description: 'Schema for a single timesheet hour entry'
      properties:
        employeeId:
          description: 'Unique identifier for the employee'
          type: integer
        date:
          description: 'Date for the timesheet entry. Must be in YYYY-MM-DD format'
          type: string
          format: date
          example: '2024-12-01'
        hours:
          description: 'Hours worked for this timesheet entry'
          type: number
          format: float
          example: 3.5
        id:
          description: 'The ID of an existing timesheet entry. This can be specified to edit an existing entry'
          type: integer
        projectId:
          description: 'The ID of the project to associate with the timesheet entry'
          type: integer
        taskId:
          description: 'The ID of the task to associate with the timesheet entry'
          type: integer
        note:
          description: 'Optional note to associate with the timesheet entry'
          type: string
      type: object
    TimeTrackingBulkResponseSchema:
      description: 'Schema for time tracking bulk operation response'
      properties:
        success:
          description: 'Indicates whether the operation was successful'
          type: boolean
        response:
          description: 'Response data containing ID and message'
          properties:
            id:
              description: 'Unique identifier for the time tracking record'
              type: string
              example: 550e8400-e29b-41d4-a716-446655440000
            message:
              description: 'Response message, typically used for error details'
              oneOf:
                -
                  type: string
                -
                  type: 'null'
          type: object
      type: object
    TimeTrackingDeleteResponseSchema:
      description: 'Schema for time tracking delete operation response'
      properties:
        status:
          description: 'Status of the delete operation'
          type: string
          example: success
        message:
          description: 'Response message indicating the result of the operation'
          type: string
          example: 'Record removed'
      type: object
    TimeTrackingIdResponseSchema:
      description: 'Schema for time tracking ID response'
      properties:
        id:
          description: 'Unique identifier for the time tracking record'
          type: string
          example: 550e8400-e29b-41d4-a716-446655440000
      type: object
    TimeTrackingRecordSchema:
      description: 'Schema for time tracking record response'
      properties:
        timeTrackingId:
          description: 'Unique identifier for the time tracking record'
          type: string
        employeeId:
          description: 'ID of the employee associated with this time tracking record'
          type: string
        divisionId:
          description: 'ID of the division associated with this time tracking record'
          oneOf:
            -
              type: string
            -
              type: 'null'
        departmentId:
          description: 'ID of the department associated with this time tracking record'
          oneOf:
            -
              type: string
            -
              type: 'null'
        jobTitleId:
          description: 'ID of the job title associated with this time tracking record'
          oneOf:
            -
              type: string
            -
              type: 'null'
        payCode:
          description: 'Pay code for this time tracking record'
          oneOf:
            -
              type: string
            -
              type: 'null'
        dateHoursWorked:
          description: 'Date the hours were worked'
          type: string
          format: date
        type:
          description: 'Type of time tracking record'
          type: string
        payRate:
          description: 'Pay rate for this time tracking record'
          type: string
          format: decimal
        rateType:
          description: 'Rate type for this time tracking record'
          type: string
        hoursWorked:
          description: 'Number of hours worked'
          type: string
          format: decimal
        adjustedHours:
          description: 'Number of adjusted hours'
          type: string
          format: decimal
        dateAdjusted:
          description: 'Date and time when the hours were adjusted'
          type: string
          format: date-time
        jobCode:
          description: 'Job code associated with this time tracking record'
          oneOf:
            -
              type: string
            -
              type: 'null'
        jobData:
          description: 'Additional job data for this time tracking record'
          oneOf:
            -
              type: string
            -
              type: 'null'
        projectId:
          description: 'ID of the project associated with this time tracking record'
          oneOf:
            -
              type: string
            -
              type: 'null'
        taskId:
          description: 'ID of the task associated with this time tracking record'
          oneOf:
            -
              type: string
            -
              type: 'null'
        shiftDifferentialId:
          description: 'ID of the shift differential associated with this time tracking record'
          oneOf:
            -
              type: string
            -
              type: 'null'
        holidayId:
          description: 'ID of the holiday associated with this time tracking record'
          oneOf:
            -
              type: string
            -
              type: 'null'
        project:
          description: 'Project information associated with this time tracking record'
          properties:
            id:
              description: 'Project information'
              type: integer
            name:
              type: string
            task:
              properties:
                id:
                  type: integer
                name:
                  type: string
              oneOf:
                -
                  type: object
                -
                  type: 'null'
          oneOf:
            -
              type: object
            -
              type: 'null'
        shiftDifferential:
          description: 'Shift differential information associated with this time tracking record'
          properties:
            id:
              description: 'Shift Differential information'
              type: integer
            name:
              type: string
          oneOf:
            -
              type: object
            -
              type: 'null'
      type: object
    ShiftDifferential-PaginatedResponseData-V1:
      description: 'Pagination envelope shared by paginated Shift Differential endpoints.'
      properties:
        meta:
          description: 'Pagination metadata.'
          required:
            - totalItems
            - totalPages
            - page
            - pageSize
          properties:
            totalItems:
              type: integer
              example: 38
            totalPages:
              type: integer
              example: 4
            page:
              type: integer
              example: 2
            pageSize:
              type: integer
              example: 10
          type: object
        _links:
          description: 'Pagination links. Keys are omitted when there is no previous/next page.'
          properties:
            prev:
              description: 'Link object for the previous page. Omitted on the first page.'
              properties:
                href:
                  type: string
              type: object
            next:
              description: 'Link object for the next page. Omitted on the last page.'
              properties:
                href:
                  type: string
              type: object
          type: object
      type: object
    ShiftDifferential-CreateTimeTrackingShiftDifferential-V1:
      description: 'Request body for creating a time tracking shift differential.'
      required:
        - name
        - rate
        - rateType
        - times
      properties:
        name:
          type: string
          maxLength: 50
          minLength: 1
          example: 'Night Shift Premium'
        rate:
          description: 'Non-negative decimal string with at most two decimal places.'
          type: string
          example: '12.00'
        rateType:
          type: string
          enum:
            - AMOUNT
            - PERCENT
          example: AMOUNT
        allowAllEmployees:
          description: 'If true, every time-tracked employee is assigned. Ignored when `employeeIds` is provided.'
          type: boolean
          default: false
        employeeIds:
          description: 'Specific employee IDs to assign. Minimum 1 entry required when provided. Takes precedence over `allowAllEmployees`.'
          type: array
          items:
            type: integer
          minItems: 1
          example:
            - 1
            - 2
            - 3
        times:
          description: 'Time windows when this differential applies. Minimum 1 entry required.'
          type: array
          items:
            required:
              - startDay
              - endDay
              - start
              - end
            properties:
              startDay:
                type: string
                enum:
                  - MONDAY
                  - TUESDAY
                  - WEDNESDAY
                  - THURSDAY
                  - FRIDAY
                  - SATURDAY
                  - SUNDAY
                example: MONDAY
              endDay:
                type: string
                enum:
                  - MONDAY
                  - TUESDAY
                  - WEDNESDAY
                  - THURSDAY
                  - FRIDAY
                  - SATURDAY
                  - SUNDAY
                example: MONDAY
              start:
                type: string
                example: '00:00'
              end:
                type: string
                example: '04:00'
            type: object
          minItems: 1
      type: object
    ShiftDifferential-UpdateTimeTrackingShiftDifferential-V1:
      description: 'Request body for partially updating a time tracking shift differential. At least one property must be provided.'
      properties:
        name:
          type: string
          maxLength: 50
          minLength: 1
          example: 'Night Shift Premium'
        rate:
          description: 'Non-negative decimal string with at most two decimal places.'
          type: string
          example: '12.00'
        rateType:
          type: string
          enum:
            - AMOUNT
            - PERCENT
          example: AMOUNT
        allowAllEmployees:
          description: 'If true, every time-tracked employee is assigned. Ignored when `employeeIds` is provided.'
          type: boolean
        employeeIds:
          description: 'Specific employee IDs to assign. Minimum 1 entry required when provided. Takes precedence over `allowAllEmployees`.'
          type: array
          items:
            type: integer
          minItems: 1
          example:
            - 1
            - 2
            - 3
        times:
          description: 'Time windows when this differential applies. Replaces all existing time windows when provided.'
          type: array
          items:
            required:
              - startDay
              - endDay
              - start
              - end
            properties:
              startDay:
                type: string
                enum:
                  - MONDAY
                  - TUESDAY
                  - WEDNESDAY
                  - THURSDAY
                  - FRIDAY
                  - SATURDAY
                  - SUNDAY
                example: MONDAY
              endDay:
                type: string
                enum:
                  - MONDAY
                  - TUESDAY
                  - WEDNESDAY
                  - THURSDAY
                  - FRIDAY
                  - SATURDAY
                  - SUNDAY
                example: MONDAY
              start:
                type: string
                example: '00:00'
              end:
                type: string
                example: '04:00'
            type: object
          minItems: 1
        archived:
          description: 'When true, archives the shift differential. When false, unarchives it.'
          type: boolean
      type: object
    FieldOptionsRequestSchema:
      description: 'Schema for field options request'
      required:
        - fields
      properties:
        fields:
          description: 'List of field names to get options for'
          type: array
          items:
            type: string
          example:
            - field_name1
            - field_name2
        dependentFields:
          description: 'Dependent fields and their values that affect the options of the requested fields'
          example:
            field_name1:
              -
                field: field_name2
                value: 123
          oneOf:
            -
              type: object
              additionalProperties:
                type: array
                items:
                  properties: { field: { type: string }, value: { type: string } }
                  type: object
            -
              type: 'null'
        filters:
          description: 'Optional filters to apply when retrieving field options. Filters limit the returned options based on other field values. The object contains a `match` key (`all` or `any`) and a `filters` array of objects with `field`, `operator`, and `value` keys.'
          example:
            match: all
            filters:
              -
                field: employee_status
                operator: includes
                value:
                  - Active
          oneOf:
            -
              type: object
            -
              type: 'null'
      type: object
    Employee:
      title: 'Employee when updating'
      description: 'A dictionary of employee field names and their new values. The properties listed below are commonly used fields, but any valid writable employee field name can be used as a key. To discover all available field names, call the List Fields endpoint (operationId: list-fields, GET /api/v1/meta/fields). Only the fields you include will be updated; omitted fields are left unchanged. Some string-valued fields are backed by lists or lookups, so callers should use valid option values from BambooHR metadata rather than assuming any free-text string will persist as entered. **Important for AI agents:** Unknown or misspelled field names are silently ignored — the endpoint returns 200 but the field is not updated. Always use the exact alias from the schema properties below or from the list-fields endpoint; do not invent aliases by adding prefixes such as `home` (e.g., `homeCity` is wrong — the correct alias is `city`). Photo data is not writable through this schema: photo-related keys are silently ignored. Use the Upload Employee Photo endpoint (operationId: upload-employee-photo) to change a profile photo.'
      properties:
        firstName:
          description: 'Legal first name.'
          type: string
        lastName:
          description: 'Legal last name.'
          type: string
        workEmail:
          description: 'Work email address.'
          type: string
        jobTitle:
          description: 'Job title.'
          type: string
        department:
          description: 'Department name.'
          type: string
        division:
          description: 'Division name.'
          type: string
        location:
          description: 'Location name.'
          type: string
        hireDate:
          description: 'Hire date in YYYY-MM-DD format.'
          type: string
          format: date
        mobilePhone:
          description: 'Mobile phone number.'
          type: string
        homePhone:
          description: 'Home phone number.'
          type: string
        workPhone:
          description: 'Work phone number.'
          type: string
        address1:
          description: 'Home street address line 1. The correct alias is `address1` — do not use `homeAddress1`, `homeStreet1`, or `street1`.'
          type: string
        address2:
          description: 'Home street address line 2 (apartment, suite, etc.). The correct alias is `address2` — do not use `homeAddress2` or `homeStreet2`.'
          type: string
        city:
          description: 'Home city. The correct alias is `city` — do not use `homeCity`.'
          type: string
        state:
          description: 'Home state or province. The correct alias is `state` — do not use `homeState`. Values are normalized to standard abbreviations (e.g., "Pennsylvania" becomes "PA").'
          type: string
        zipcode:
          description: 'Home ZIP or postal code. The correct alias is `zipcode` — do not use `homeZipcode` or `homeZip`.'
          type: string
        country:
          description: 'Home country name. The correct alias is `country` — do not use `homeCountry`.'
          type: string
      type: object
      example:
        firstName: Panda
        lastName: Bear
        workEmail: panda.bear@example.com
        jobTitle: 'Software Engineer'
        department: Engineering
      additionalProperties:
        anyOf:
          -
            type: string
          -
            type: number
          -
            type: boolean
          -
            type: integer
          -
            type: array
            items:
              anyOf:
                -
                  type: string
                -
                  type: number
                -
                  type: boolean
                -
                  type: integer
                -
                  type: object
                  additionalProperties: true
                -
                  type: 'null'
          -
            type: object
            additionalProperties: true
          -
            type: 'null'
    postNewEmployee:
      title: 'New Employee'
      description: 'A dictionary of employee field names and their values for the new employee. At minimum, firstName and lastName are required. The properties listed below are commonly used fields, but any valid writable employee field name can be used as a key. To discover all available field names, call the List Fields endpoint (operationId: list-fields, GET /api/v1/meta/fields). Additional fields may use string, number, boolean, array, object, or null values depending on the field type. Some string-valued fields are backed by lists or lookups, so callers should use valid option values from BambooHR metadata rather than assuming any free-text string will persist as entered. Photo data is not writable through this schema: photo-related keys are silently ignored. After creation, use the Upload Employee Photo endpoint (operationId: upload-employee-photo) to attach a profile photo.'
      required:
        - firstName
        - lastName
      properties:
        firstName:
          description: 'Legal first name (required).'
          type: string
        lastName:
          description: 'Legal last name (required).'
          type: string
        workEmail:
          description: 'Work email address.'
          type: string
        jobTitle:
          description: 'Job title.'
          type: string
        department:
          description: 'Department name.'
          type: string
        hireDate:
          description: 'Hire date in YYYY-MM-DD format.'
          type: string
          format: date
      type: object
      example:
        firstName: Panda
        lastName: Bear
        jobTitle: 'Software Engineer'
        department: Engineering
      additionalProperties:
        anyOf:
          -
            type: string
          -
            type: number
          -
            type: boolean
          -
            type: integer
          -
            type: array
            items:
              anyOf:
                -
                  type: string
                -
                  type: number
                -
                  type: boolean
                -
                  type: integer
                -
                  type: object
                  additionalProperties: true
                -
                  type: 'null'
          -
            type: object
            additionalProperties: true
          -
            type: 'null'
    ListFieldValues:
      title: 'Create or Update List Field Values'
      description: 'Payload for creating, updating, or archiving options on a list field.'
      properties:
        options:
          description: 'The list field options to create, update, or archive.'
          type: array
          items:
            properties:
              id:
                description: 'The existing option ID. Omit this field to create a new option. Required when archiving an existing option.'
                type: integer
              value:
                description: 'The display value for the option. Required when creating a new option (i.e., when id is omitted).'
                type: string
              archived:
                description: 'Whether the option should be archived. Use `yes` to soft-delete an option or `no` to reactivate a previously archived option. Archived options continue to appear in GET responses for historical data integrity.'
                type: string
                enum:
                  - 'yes'
                  - 'no'
              adpCode:
                description: 'Optional payroll-mapping code associated with the option.'
                type: string
            type: object
      type: object
    ListFieldValuesXml:
      title: 'Create or Update List Field Values (XML)'
      description: 'XML payload for creating, updating, or archiving options on a list field. The root element is <options>, containing one or more <option> elements. Each <option> element carries optional id, archived, and adpCode XML attributes; the display value is the text content of the element.'
      properties:
        option:
          description: 'One or more options to create, update, or archive.'
          type: array
          items:
            properties:
              id:
                description: 'The existing option ID. Omit to create a new option. Required when archiving.'
                type: integer
                xml:
                  attribute: true
              value:
                description: 'The display value for the option. In XML format, this is the text content of the <option> element.'
                type: string
              archived:
                description: 'Whether to archive this option. Use yes to soft-delete or no to reactivate.'
                type: string
                enum:
                  - 'yes'
                  - 'no'
                xml:
                  attribute: true
              adpCode:
                description: 'Optional payroll-mapping code.'
                type: string
                xml:
                  attribute: true
            type: object
            xml:
              name: option
          xml:
            name: option
            wrapped: false
      type: object
      xml:
        name: options
    TimeOffRequest:
      title: 'Create Time Off Request'
      required:
        - status
        - start
        - end
        - timeOffTypeId
      properties:
        status:
          description: 'The initial status of the request.'
          type: string
          enum:
            - approved
            - denied
            - declined
            - requested
        start:
          description: 'Start date in YYYY-MM-DD format.'
          type: string
          format: date
        end:
          description: 'End date in YYYY-MM-DD format. Must be on or after the start date.'
          type: string
          format: date
        timeOffTypeId:
          description: 'The ID of the time off type for this request.'
          type: integer
        amount:
          description: 'Total hours or days requested. Ignored when `dates` array is provided (sum of daily amounts is used instead).'
          type: number
        previousRequest:
          description: 'The ID of a previous time off request to supersede. The previous request will be cancelled.'
          type: integer
        notes:
          description: 'Optional notes from the employee or manager.'
          type: array
          items:
            properties:
              from:
                description: 'Who the note is from.'
                type: string
                enum:
                  - employee
                  - manager
              note:
                description: 'The note text.'
                type: string
            type: object
        dates:
          description: 'Optional per-day breakdown. When provided, the top-level `amount` is ignored and the sum of daily amounts is used.'
          type: array
          items:
            properties:
              ymd:
                description: 'Date in YYYY-MM-DD format.'
                type: string
                format: date
              amount:
                description: 'Hours or days for this date.'
                type: number
            type: object
      type: object
    request:
      title: 'Request Status'
      required:
        - status
      properties:
        status:
          description: 'The new status for the time off request.'
          type: string
          enum:
            - approved
            - denied
            - declined
            - canceled
            - cancelled
        note:
          description: 'A note to attach to the change in status.'
          type: string
      type: object
      example:
        status: approved
        note: 'Looks good, approved!'
    TimeOffHistory:
      required:
        - date
      properties:
        date:
          description: 'The date for the history item in YYYY-MM-DD format.'
          type: string
          format: date
        eventType:
          description: 'The type of history event. Defaults to `used` for the /history path and `override` for the /balance_adjustment path when omitted.'
          type: string
          enum:
            - used
            - override
        timeOffRequestId:
          description: 'The ID of an approved time off request. Required when eventType is `used`.'
          type: integer
        timeOffTypeId:
          description: 'The ID of the time off type. Required when eventType is `override`.'
          type: integer
        amount:
          description: 'The number of hours/days to record. Required when eventType is `override`.'
          type: number
        note:
          description: 'An optional note to show in history.'
          type: string
      type: object
    AdjustTimeOffBalance:
      required:
        - amount
        - date
        - timeOffTypeId
      properties:
        date:
          description: 'The date the adjustment should be added in history. Should be in ISO8601 date format (YYYY-MM-DD).'
          type: string
          format: date
        timeOffTypeId:
          description: 'The ID of the time off type to add a balance adjustment for.'
          type: integer
        amount:
          description: 'The number of hours/days to adjust the balance by.'
          type: number
        note:
          description: 'This is an optional note to show in history.'
          type: string
      type: object
    PostNewEmployeeFileCategory:
      title: 'New Employee File Category'
      type: array
      items:
        type: string
      example:
        - 'A new category'
    EmployeeFileUpdate:
      title: 'Employee File Update'
      properties:
        name:
          description: 'The new display name for the file.'
          type: string
        categoryId:
          description: 'The ID of the file category (section) to move the file into.'
          type: integer
        shareWithEmployee:
          description: "Whether the file is shared with the employee. Also accepted as 'shareWithEmployees'."
          type: string
          enum:
            - 'yes'
            - 'no'
        shareWithEmployees:
          description: 'Alias for shareWithEmployee. Whether the file is shared with the employee.'
          type: string
          enum:
            - 'yes'
            - 'no'
      type: object
    NewCompanyFileCategory:
      title: 'New Company File Category'
      type: array
      items:
        type: string
      example:
        - 'A new category'
    CompanyFileUpdate:
      title: 'Company File Update'
      properties:
        name:
          description: 'The new display name for the file.'
          type: string
        categoryId:
          description: 'The ID of the category (file section) to move the file to.'
          type: string
        shareWithEmployee:
          description: 'Whether the file is shared with employees.'
          type: string
          enum:
            - 'yes'
            - 'no'
      type: object
    RequestCustomReport:
      title: 'Request Custom Report'
      description: 'Report definition for an ad-hoc custom report. Specify the fields to include and optionally filter the employee set. Field IDs are the internal BambooHR field names (e.g. `firstName`, `lastName`, `department`). See the fields metadata endpoint for available field IDs.'
      properties:
        title:
          description: 'A label for the report. Included in the response and used as the file name for downloaded reports.'
          type: string
        fields:
          description: 'Array of field IDs to include as columns in the report. Maximum of 400 fields.'
          type: array
          items:
            type: string
        filters:
          description: 'Optional filters to restrict which employees appear in the report.'
          properties:
            lastChanged:
              description: 'Filters employees to those whose data was last changed on or after the given date/time.'
              properties:
                value:
                  description: 'ISO 8601 date-time value. Only employees whose data has changed at or after this date-time will be included.'
                  type: string
                  format: date-time
                includeNull:
                  description: 'Whether to include employees with no last-changed date. `yes` (default) includes them; `no` excludes them.'
                  type: string
                  enum: ['yes', 'no']
              type: object
            employeeIds:
              description: 'Restricts the report to only the specified employee IDs.'
              type: array
              items:
                type: integer
          type: object
        filterDuplicates:
          description: 'Whether to apply standard duplicate row filtering. Defaults to enabled. Set to `no` to return raw results without deduplication.'
          type: string
          enum:
            - 'yes'
            - 'no'
      type: object
    TableRowUpdate:
      title: 'Table Row Update'
      description: 'A dictionary of table field names and values for creating or updating a row in an employee table. The listed properties are common examples, but accepted fields depend on the specific table being targeted. Some string-valued fields are backed by lists or lookups, so callers should use valid option values from BambooHR metadata rather than assuming any free-text string will persist as entered.'
      properties:
        date:
          description: 'The effective date for the row in YYYY-MM-DD format.'
          type: string
          format: date
        location:
          description: 'The employee location value for the row.'
          type: string
        division:
          description: 'The division value for the row.'
          type: string
        department:
          description: 'The department value for the row.'
          type: string
        jobTitle:
          description: 'The job title value for the row.'
          type: string
        reportsTo:
          description: 'The manager or reports-to value for the row.'
          type: string
        teams:
          description: 'Team values associated with the row.'
          type: array
          items:
            type: string
      type: object
      additionalProperties:
        anyOf:
          -
            type: string
          -
            type: number
          -
            type: boolean
          -
            type: integer
          -
            type: array
            items:
              anyOf:
                -
                  type: string
                -
                  type: number
                -
                  type: boolean
                -
                  type: integer
                -
                  type: object
                  additionalProperties: true
                -
                  type: 'null'
          -
            type: object
            additionalProperties: true
          -
            type: 'null'
    Login:
      title: Login
      required:
        - applicationKey
        - user
        - password
      properties:
        applicationKey:
          description: 'The API key of the registered application making the login request. Mobile application keys (iOS/Android) are not accepted.'
          type: string
        user:
          description: "The user's email address or username."
          type: string
        password:
          description: "The user's password."
          type: string
          format: password
        deviceId:
          description: 'Optional device identifier. When provided, the generated API key is associated with this device.'
          type: string
      type: object
    EmployeeDependent:
      title: 'Employee Dependent'
      required:
        - employeeId
      properties:
        employeeId:
          description: 'The ID of the employee this dependent belongs to. Required.'
          type: string
        firstName:
          description: "The dependent's first name."
          type: string
        middleName:
          description: "The dependent's middle name."
          type: string
        lastName:
          description: "The dependent's last name."
          type: string
        relationship:
          description: 'The dependent''s relationship to the employee (e.g. "spouse", "child", "domestic_partner").'
          type: string
        gender:
          description: "The dependent's gender."
          type: string
        ssn:
          description: 'The dependent''s Social Security Number, provided as plain text. Stored encrypted. Returned as a masked value (e.g. "xxx-xx-1234") on read.'
          type: string
        sin:
          description: "The dependent's Social Insurance Number (Canadian equivalent of SSN), provided as plain text. Stored encrypted. Returned as a masked value on read."
          type: string
        dateOfBirth:
          description: "The dependent's date of birth in YYYY-MM-DD format."
          type: string
          format: date
        addressLine1:
          description: "The first line of the dependent's address."
          type: string
        addressLine2:
          description: "The second line of the dependent's address."
          type: string
        city:
          description: "The dependent's city."
          type: string
        state:
          description: 'The dependent''s state, provided as a state code (e.g. "UT"). Returned as a full state name on read.'
          type: string
        zipCode:
          description: "The dependent's ZIP or postal code."
          type: string
        homePhone:
          description: "The dependent's home phone number."
          type: string
        country:
          description: 'The dependent''s country, provided as an ISO 3166-1 alpha-2 country code (e.g. "US"). Returned as a full country name on read.'
          type: string
        isUsCitizen:
          description: 'Whether the dependent is a US citizen. Accepted values: "yes" or "no".'
          type: string
          enum:
            - 'yes'
            - 'no'
        isStudent:
          description: 'Whether the dependent is currently a student. Accepted values: "yes" or "no".'
          type: string
          enum:
            - 'yes'
            - 'no'
      type: object
    EmployeeDependentsResponse:
      title: 'Employee Dependents Response'
      properties:
        'Employee Dependents':
          description: 'Array of employee dependent objects.'
          type: array
          items:
            properties:
              id:
                description: 'The unique ID of the dependent record.'
                type: string
              employeeId:
                description: 'The ID of the employee this dependent belongs to.'
                type: string
              firstName:
                description: "The dependent's first name."
                type: string
              middleName:
                description: "The dependent's middle name. Null if not set."
                oneOf:
                  - { type: string }
                  - { type: 'null' }
              lastName:
                description: "The dependent's last name."
                type: string
              relationship:
                description: "The dependent's relationship to the employee."
                type: string
              gender:
                description: "The dependent's gender."
                type: string
              maskedSSN:
                description: 'The dependent''s masked SSN (e.g. "xxx-xx-1234"). Null if no SSN on file.'
                oneOf:
                  - { type: string }
                  - { type: 'null' }
              maskedSIN:
                description: "The dependent's masked SIN. Null if no SIN on file."
                oneOf:
                  - { type: string }
                  - { type: 'null' }
              dateOfBirth:
                description: "The dependent's date of birth in YYYY-MM-DD format."
                type: string
                format: date
              addressLine1:
                description: "The first line of the dependent's address. Null if not set."
                oneOf:
                  - { type: string }
                  - { type: 'null' }
              addressLine2:
                description: "The second line of the dependent's address. Null if not set."
                oneOf:
                  - { type: string }
                  - { type: 'null' }
              city:
                description: "The dependent's city. Null if not set."
                oneOf:
                  - { type: string }
                  - { type: 'null' }
              state:
                description: 'The dependent''s state as a full name (e.g. "Utah"). Null if not set.'
                oneOf:
                  - { type: string }
                  - { type: 'null' }
              zipCode:
                description: "The dependent's ZIP or postal code. Null if not set."
                oneOf:
                  - { type: string }
                  - { type: 'null' }
              homePhone:
                description: "The dependent's home phone number. Null if not set."
                oneOf:
                  - { type: string }
                  - { type: 'null' }
              country:
                description: 'The dependent''s country as a full name (e.g. "United States"). Null if not set.'
                oneOf:
                  - { type: string }
                  - { type: 'null' }
              isUsCitizen:
                description: 'Whether the dependent is a US citizen. "yes" or "no". Null if not set.'
                oneOf:
                  - { type: string, enum: ['yes', 'no'] }
                  - { type: 'null' }
              isStudent:
                description: 'Whether the dependent is currently a student. "yes" or "no". Null if not set.'
                oneOf:
                  - { type: string, enum: ['yes', 'no'] }
                  - { type: 'null' }
            type: object
      type: object
    CompanyBenefitSummary:
      title: 'Company Benefit Summary'
      description: 'A summary representation of a company benefit plan as returned by the list endpoint.'
      properties:
        id:
          description: 'The unique identifier of the company benefit plan.'
          type: string
        name:
          description: 'The name of the company benefit plan.'
          type: string
        type:
          description: 'The benefit category type.'
          type: string
          enum:
            - health
            - dental
            - vision
            - retirement
            - hsa
            - flex
            - life
            - disability
            - reimbursement
            - supplemental
            - other
        benefitVendorId:
          description: 'The ID of the benefit vendor associated with this plan, or null if none.'
          oneOf:
            -
              type: string
            -
              type: 'null'
        deductionTypeId:
          description: 'The deduction type ID for this plan, or null if not applicable.'
          oneOf:
            -
              type: string
            -
              type: 'null'
        companyDeductionId:
          description: 'The company-level deduction record ID linked to this plan, or null if not set.'
          oneOf:
            -
              type: string
            -
              type: 'null'
        startDate:
          description: 'The date the benefit plan becomes effective (YYYY-MM-DD), or null if not set.'
          oneOf:
            -
              type: string
              format: date
            -
              type: 'null'
        endDate:
          description: 'The date the benefit plan ends (YYYY-MM-DD), or null if ongoing.'
          oneOf:
            -
              type: string
              format: date
            -
              type: 'null'
        allowsCatchUp:
          description: 'Whether the plan allows catch-up contributions (e.g., for HSA plans for employees 55+), or null if not applicable to this plan type.'
          oneOf:
            -
              type: boolean
            -
              type: 'null'
        allowsSuperCatchUp:
          description: 'Whether the plan allows super catch-up contributions, or null if not applicable to this plan type.'
          oneOf:
            -
              type: boolean
            -
              type: 'null'
      type: object
    EmployeeBenefitFilters:
      title: 'Employee Benefit Filters'
      required:
        - filters
      properties:
        filters:
          description: 'Scope filters for the request. At least one of `employeeId`, `companyBenefitId`, or `enrollmentStatusEffectiveDate` must be provided; none of the three is individually required, but omitting all three returns a 400 validation error. Any combination of the three is accepted.'
          minProperties: 1
          properties:
            employeeId:
              description: 'Return benefit enrollments for a specific employee identified by their numeric ID. Optional unless neither `companyBenefitId` nor `enrollmentStatusEffectiveDate` is supplied.'
              type: integer
            companyBenefitId:
              description: 'Return benefit enrollments for a specific company benefit plan identified by its numeric ID. Optional unless neither `employeeId` nor `enrollmentStatusEffectiveDate` is supplied.'
              type: integer
            enrollmentStatusEffectiveDate:
              description: 'Return benefit enrollments whose enrollment status became effective on this date. Must be in `YYYY-MM-DD` format. Optional unless neither `employeeId` nor `companyBenefitId` is supplied.'
              type: string
              format: date
          type: object
      type: object
    MemberBenefitEvent:
      title: 'Member Benefit Event'
      description: 'A single member (employee or dependent) and their benefit coverage events across all enrolled plans.'
      properties:
        memberId:
          description: "The unique identifier of the member. Format: 'employee.{id}' for employees, 'dependent.{id}' for dependents."
          type: string
        coverages:
          description: 'The benefit plan coverages for this member, one entry per plan.'
          type: array
          items:
            properties:
              planId:
                description: 'The ID of the benefit plan.'
                type: string
              events:
                description: "Chronological list of benefit events for this member and plan, covering the past year. Event type 'enrollment' includes premiumTierId and monthlyPremiumInCents; 'eligibility' and 'loss' events include only effectiveDate and type."
                type: array
                items:
                  properties: { effectiveDate: { description: 'The date this event takes effect (YYYY-MM-DD).', type: string, format: date }, type: { description: 'The type of benefit event.', type: string, enum: [eligibility, enrollment, loss] }, premiumTierId: { description: "The premium tier ID for enrollment events. Only present on events with type 'enrollment'.", oneOf: [{ type: string }, { type: 'null' }] }, monthlyPremiumInCents: { description: "The monthly premium amount in cents for enrollment events. Only present on events with type 'enrollment'.", oneOf: [{ type: number }, { type: 'null' }] } }
                  type: object
            type: object
      type: object
    TimeTrackingRecord:
      title: 'Time Tracking Record'
      required:
        - dateHoursWorked
        - employeeId
        - hoursWorked
        - rateType
        - timeTrackingId
      properties:
        timeTrackingId:
          description: 'A unique identifier for the record. Use this ID to adjust or delete these hours. It can be any ID you use to track the record up to 36 characters in length. (i.e. UUID).'
          type: string
        employeeId:
          description: 'The ID of the employee.'
          type: integer
        divisionId:
          description: '[Optional] The ID of the division for the employee.'
          type: integer
        departmentId:
          description: '[Optional] The ID of the department for the employee.'
          type: integer
        jobTitleId:
          description: '[Optional] The ID of the job title for the employee.'
          type: integer
        payCode:
          description: '[Optional] Only necessary if the payroll provider requires a pay code'
          type: string
        dateHoursWorked:
          description: 'The date the hours were worked. Please use the ISO-8601 date format YYYY-MM-DD.'
          type: string
        payRate:
          description: '[Optional] The rate of pay. e.g. $15.00/hour should use 15.00 here. Only necessary if the payroll provider requires a pay rate.'
          type: number
        rateType:
          description: 'The type of hours - regular or overtime. Please use either "REG", "OT", or "DT" here.'
          type: string
        hoursWorked:
          description: 'The number of hours worked.'
          type: number
        jobCode:
          description: '[Optional] A job code.'
          type: integer
        jobData:
          description: '[Optional] A list of up to four 20 characters max job numbers in comma delimited format with no spaces.'
          type: string
      type: object
    TimeTrackingRecordBulk:
      title: 'Time Tracking Record Bulk'
      type: array
      items:
        $ref: '#/components/schemas/TimeTrackingRecord'
    TimeTrackingTask:
      title: 'Time Tracking Task'
      properties:
        id:
          description: 'ID of the task.'
          type: integer
        name:
          description: 'Name of the task.'
          type: string
      type: object
    TimeTrackingProjectWithTasksAndEmployeeIds:
      title: 'Time tracking project with tasks and list of employee IDs.'
      properties:
        id:
          description: 'ID of the project.'
          type: integer
        name:
          description: 'Name of the project.'
          type: string
        tasks:
          description: 'A list of time tracking tasks for the project.'
          type: array
          items:
            $ref: '#/components/schemas/TimeTrackingTask'
        employeeIds:
          description: 'A list of employee IDs that can log time for this project. If not present, all employees can log time for the project.'
          type: array
          items:
            type: integer
      type: object
    UpdateGoalV1:
      title: 'Update Employee Goal (v1)'
      required:
        - dueDate
        - sharedWithEmployeeIds
        - title
      properties:
        title:
          description: '[Required] The goal title.'
          type: string
        description:
          description: '[Optional] The goal description. If omitted, the existing value will be overwritten with an empty string.'
          type: string
        percentComplete:
          description: '[Optional] The goal completion percentage (0-100). If omitted, the existing value will be overwritten with 0.'
          type: integer
        alignsWithOptionId:
          description: '[Optional] The option ID that aligns with this goal. If omitted, the existing value will be overwritten with null.'
          oneOf:
            -
              type: string
            -
              type: 'null'
        sharedWithEmployeeIds:
          description: '[Required] Employee IDs of employees with whom the goal is shared. All goal owners are considered "shared with". This must include the employee for whom the goal is created.'
          type: array
          items:
            type: integer
        dueDate:
          description: '[Required] The goal due date in YYYY-MM-DD format.'
          type: string
          format: date
        completionDate:
          description: '[Optional] The date the goal was completed, in YYYY-MM-DD format. Required when percentComplete is 100. If omitted, the existing value will be overwritten with null.'
          oneOf:
            -
              type: string
              format: date
            -
              type: 'null'
      type: object
    Location:
      properties:
        id:
          description: 'The ID of the location'
          type: integer
        name:
          description: 'Name of the location'
          type: string
        description:
          description: 'Description of the location'
          type: string
        city:
          description: 'City of the location'
          type: string
        state:
          $ref: '#/components/schemas/State'
        country:
          $ref: '#/components/schemas/Country'
        zipcode:
          description: 'The ZIP or postal code of the location'
          type: string
        addressLine1:
          description: 'The first address line of the location'
          type: string
        addressLine2:
          description: 'The second address line of the location'
          type: string
        phone:
          description: 'The phone number of the location'
          type: string
      type: object
    State:
      properties:
        id:
          description: 'The ID of the state'
          type: integer
        name:
          description: 'The name of the state'
          type: string
        abbrev:
          description: 'The abbreviation of the state'
          type: string
        iso_code:
          description: 'The ISO standard code of the state'
          type: string
      type: object
    Country:
      properties:
        id:
          description: 'The ID of the country'
          type: integer
        name:
          description: 'The name of the country'
          type: string
        iso_code:
          description: 'The ISO standard code of the country'
          type: string
      type: object
    TrainingType:
      properties:
        id:
          description: 'The ID of the training'
          type: integer
        name:
          description: 'Name of the training type.'
          type: string
        renewable:
          description: 'If true, training will be renewed based off of frequency.'
          type: boolean
        frequency:
          description: 'The frequency is the (optional) amount of months between renewing trainings. Not valid if training are not renewable.'
          type: integer
        dueFromHireDate:
          description: 'Number of days before the training is due for new hires. Not valid if training is not required.'
          type: integer
        required:
          description: 'Whether this is a required training. Null when not set.'
          oneOf:
            -
              type: boolean
            -
              type: 'null'
        category:
          description: 'The training category when set, or an empty array when no category is assigned.'
          oneOf:
            -
              $ref: '#/components/schemas/TrainingCategory'
            -
              description: 'Empty array when no category is set'
              type: array
              items: {  }
              maxItems: 0
        linkUrl:
          description: 'Optional URL that can be included with a training. Null when not set.'
          oneOf:
            -
              type: string
            -
              type: 'null'
        description:
          description: 'Description for the training. Null when not set.'
          oneOf:
            -
              type: string
            -
              type: 'null'
        allowEmployeesToMarkComplete:
          description: 'Allows all employees who can view the training to be able to mark it complete.'
          type: boolean
      type: object
    TrainingCategory:
      description: 'The category ID and name'
      properties:
        id:
          description: 'The ID of the training category.'
          type: integer
        name:
          description: 'The name of the training category.'
          type: string
      type: object
    TrainingRecord:
      properties:
        id:
          description: 'The ID of the training record.'
          type: integer
        employeeId:
          description: 'The ID of the employee associated with the training, returned as a string.'
          type: string
        completed:
          description: 'Completed is a date in the format yyyy-mm-dd.'
          type: string
          format: date
        notes:
          description: 'Notes left on the training record. Null when not set.'
          oneOf:
            -
              type: string
            -
              type: 'null'
        instructor:
          description: 'Name of the instructor. Null when not set or when disabled by company training settings.'
          oneOf:
            -
              type: string
            -
              type: 'null'
        credits:
          description: 'Credits earned for the training record, returned as a string. Null when not set or when disabled by company training settings.'
          oneOf:
            -
              type: string
            -
              type: 'null'
        hours:
          description: 'Hours associated with the training record, returned as a string. Null when not set or when disabled by company training settings.'
          oneOf:
            -
              type: string
            -
              type: 'null'
        cost:
          description: "The cost in the format '{amount} {currency}', e.g. '50.00 USD'. May be null or empty string when not set."
          oneOf:
            -
              type: string
            -
              type: 'null'
        type:
          description: 'The training type ID. Returned as a string on update/list responses, and as an integer on create responses.'
          oneOf:
            -
              description: 'String form'
              type: string
            -
              description: 'Integer form'
              type: integer
      type: object
    WebhookEventType:
      title: 'Webhook Event Type'
      description: 'The type of event that triggers a webhook. Cannot mix employee_with_fields events with employee events. This list will be expanded in the future.'
      type: string
      enum:
        - employee_with_fields.updated
        - employee_with_fields.created
        - employee_with_fields.deleted
        - company-integrations.updated
        - employee.created
        - employee.updated
        - employee.deleted
        - company.updated
        - company.deleted
    NewWebHook:
      title: 'Add Webhook'
      required:
        - name
        - url
        - format
      properties:
        name:
          description: 'The name of the webhook.'
          type: string
          example: 'My new webhook'
        monitorFields:
          description: 'A list of fields to monitor. At least one field is required to be monitored if events is empty or contains employee_with_fields.updated or employee.updated.'
          type: array
          items:
            type: string
          example:
            - firstName
            - lastName
        postFields:
          description: 'An object map of field ID or alias to the external name used in the webhook payload (e.g. `{"firstName": "First Name"}`). Omit or send an empty object to include no extra fields.'
          type: object
          default: {  }
          example:
            firstName: Name
            lastName: Surname
            dateOfBirth: DOB
        url:
          description: 'The url the webhook should send data to (must begin with https://).'
          type: string
        format:
          description: 'The payload format the webhook uses. Required.'
          type: string
          enum:
            - json
            - form-encoded
          example: json
        includeCompanyDomain:
          description: 'If set to true, the company domain will be added to the webhook request header.'
          type: boolean
          example: false
        events:
          description: "Events that trigger this webhook. Defaults to ['employee_with_fields.updated', 'employee_with_fields.deleted', 'employee_with_fields.created'] if not specified. Cannot mix employee_with_fields events with employee events."
          type: array
          items:
            $ref: '#/components/schemas/WebhookEventType'
          example:
            - employee.created
            - employee.updated
            - employee.deleted
      type: object
    WebHookResponse:
      title: 'Webhook Response'
      properties:
        id:
          description: 'The ID of the webhook.'
          type: string
          example: '4'
        name:
          description: 'The name of the webhook.'
          type: string
          example: 'Example Webhook'
        created:
          description: 'Datetime when the webhook was created (UTC, format: YYYY-MM-DD HH:MM:SS).'
          type: string
          example: '2021-09-20 22:38:01'
        lastSent:
          description: 'Datetime when the webhook was last fired (UTC, format: YYYY-MM-DD HH:MM:SS). Null if the webhook has never fired.'
          example: '2021-09-20 22:38:01'
          oneOf:
            -
              type: string
            -
              type: 'null'
        monitorFields:
          description: 'A list of fields being monitored. Null when the webhook is not configured to monitor fields (e.g. event-only webhooks).'
          example:
            - firstName
            - lastName
          oneOf:
            -
              type: array
              items:
                type: string
            -
              type: 'null'
        postFields:
          description: 'A map of field ID or alias to the external name used in the webhook payload.'
          type: object
          example:
            firstName: Name
            lastName: Surname
            dateOfBirth: DOB
        url:
          description: 'The URL the webhook sends data to.'
          type: string
          example: 'https://www.example.com'
        format:
          description: 'The payload format used by the webhook.'
          type: string
          enum:
            - json
            - form-encoded
          example: json
        includeCompanyDomain:
          description: 'Whether the company domain is added to the webhook request header.'
          type: boolean
          example: false
        events:
          description: 'Events that trigger this webhook.'
          type: array
          items:
            $ref: '#/components/schemas/WebhookEventType'
          example:
            - employee_with_fields.created
            - employee_with_fields.updated
        errors:
          description: 'Field-level permission errors associated with the webhook configuration, when present.'
          type: array
          items:
            $ref: '#/components/schemas/WebhookSubErrorProperty'
          example:
            -
              error: 'Permission denied to the following fields'
              postFields:
                -
                  id: 2
                  name: lastName
      type: object
    WebhookBadRequest:
      properties:
        errors:
          type: array
          items:
            properties:
              error:
                type: string
              issues:
                type: array
                items:
                  type: string
            type: object
      type: object
      example:
        errors:
          -
            error: 'Invalid request body'
            issues:
              - 'problem one'
              - 'problem two'
              - …
    WebhookError:
      title: 'Webhook Standard error'
      properties:
        errors:
          oneOf:
            -
              $ref: '#/components/schemas/WebhookSubErrorProperty'
            -
              type: array
              items:
                $ref: '#/components/schemas/WebhookSubErrorProperty'
      type: object
      example:
        errors:
          -
            error: 'You do not have access to webhook ID: 5'
          -
            error: 'No webhook was found with ID: 7.'
          -
            error: 'Sorry for the inconvenience, please try again later or contact BambooHR'
      additionalProperties: false
    WebhookSubErrorProperty:
      properties:
        error:
          type: string
        unknownFields:
          type: array
          items:
            properties:
              id:
                oneOf:
                  - { type: integer }
                  - { type: string }
                  - { type: 'null' }
              name:
                type: string
            type: object
        monitorFields:
          type: array
          items:
            properties:
              id:
                oneOf:
                  - { type: integer }
                  - { type: string }
              name:
                type: string
            type: object
        duplicatePostString:
          type: array
          items:
            type: string
        postFields:
          type: array
          items:
            properties:
              id:
                oneOf:
                  - { type: integer }
                  - { type: string }
              name:
                type: string
            type: object
      type: object
      additionalProperties: false
    DataRequest:
      title: 'Data Request'
      required:
        - fields
      properties:
        fields:
          description: 'Field names to include in each returned record. Use "Get Fields from Dataset" to discover available names.'
          type: array
          items:
            type: string
          example:
            - lastNameFirstName
            - firstName
            - employeeNumber
            - hireDate
            - ssn
        aggregations:
          description: 'Aggregation configuration. Set `defaultAggregation` to apply one aggregation type to every requested field, or use `overridingAggregations` to target specific fields. Both may be combined.'
          properties:
            defaultAggregation:
              description: 'Aggregation type applied to all requested fields when set. Pass null to skip the default.'
              example: count
              oneOf:
                -
                  type: string
                  enum: [count, count_unique, sum, avg, min, max]
                -
                  type: 'null'
            overridingAggregations:
              description: 'Per-field aggregation overrides. Each element is an object whose single key is the field name and whose value is the aggregation type.'
              type: array
              items:
                type: object
                additionalProperties:
                  type: string
              example:
                -
                  hireDate: count
          type: object
        sortBy:
          description: 'Ordered list of sort rules. Priority follows array order. Include aggregationType when sorting by an aggregated value in a grouped request; it must match an aggregation requested for the same field.'
          type: array
          items:
            properties:
              field:
                description: 'Field name to sort by.'
                type: string
              sort:
                description: 'Sort direction.'
                type: string
                enum:
                  - asc
                  - desc
              aggregationType:
                description: 'Optional. Use when sorting by an aggregated value; must match the requested aggregation for this field.'
                type: string
                enum:
                  - count
                  - count_unique
                  - sum
                  - avg
                  - min
                  - max
            type: object
          example:
            -
              field: hireDate
              sort: desc
              aggregationType: count
        filters:
          description: 'Filter configuration. Combine multiple conditions with a `match` strategy.'
          properties:
            match:
              description: 'Logical operator for combining filters: `all` (AND) or `any` (OR).'
              type: string
              enum:
                - all
                - any
            filters:
              description: 'Array of filter conditions. Filter fields do not need to appear in the top-level `fields` array.'
              type: array
              items:
                properties:
                  field: { description: 'Field name to filter on.', type: string }
                  operator: { description: 'Comparison operator. Available operators depend on field type (see endpoint description).', type: string, enum: [contains, does_not_contain, equal, not_equal, empty, not_empty, lt, lte, gt, gte, last, next, range, checked, not_checked, includes, does_not_include] }
                  value: { description: "Filter value. The accepted shape depends on the operator:\n- `includes`/`does_not_include`: pass the value as an array enclosed in square brackets, for example `[\"Full-Time\", \"Part-Time\"]`.\n- Date `lt`/`lte`/`gt`/`gte`: either a date string in `YYYY-MM-DD` format (for example `\"2024-01-01\"`), or a relative object `{\"duration\": \"5\", \"unit\": \"years\"}` where `unit` is `days`, `weeks`, `months`, or `years` and `duration` is a number as a string. For `lt`/`lte` the relative value is measured backward from today (today minus the duration); for `gt`/`gte` it is measured forward from today (today plus the duration).\n- `last`/`next`: a relative object `{\"duration\": \"5\", \"unit\": \"years\"}` where `unit` is `days`, `weeks`, `months`, or `years` and `duration` is a number as a string.\n- `range`: an object `{\"start\": \"2023-01-01\", \"end\": \"2023-12-31\"}` with dates in `YYYY-MM-DD` format.\n- All other operators: a scalar string value." }
                type: object
          type: object
          example:
            match: all
            filters:
              -
                field: firstName
                operator: equal
                value: Pudgy
              -
                field: employee_state
                operator: includes
                value:
                  - CA
                  - FL
              -
                field: hireDate
                operator: gte
                value:
                  duration: '5'
                  unit: years
        groupBy:
          description: 'Field names to group results by. Currently supports only one field. When grouping is active, the `data` key in the response becomes an object keyed by group value instead of an array.'
          type: array
          items:
            type: string
          example:
            - hireDate
        showHistory:
          description: 'Entity names of historical table fields whose history rows should be included. Entity names are returned by "Get Fields from Dataset".'
          type: array
          items:
            type: string
          example:
            - education
      type: object
    DatasetEmployee:
      type: object
      example:
        lastNameFirstName: 'Panda, Pudgy'
        firstName: Pudgy
        employeeNumber: '123'
        middleName: 'null'
        ssn: 123-45-6478
    Pagination:
      properties:
        total_records:
          type: integer
          example: '10'
        current_page:
          type: integer
          example: '2'
        total_pages:
          type: integer
          example: '10'
        next_page:
          example: 'https://{companyDomain}.bamboohr.com/api/v1/{endpointPath}?page=3&page_size=1'
          oneOf:
            -
              type: string
            -
              type: 'null'
        prev_page:
          example: 'https://{companyDomain}.bamboohr.com/api/v1/{endpointPath}?page=1&page_size=1'
          oneOf:
            -
              type: string
            -
              type: 'null'
      type: object
    EmployeeResponse:
      title: 'Employee Response'
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/DatasetEmployee'
        aggregations:
          type: array
          items:
            properties:
              field:
                type: string
              aggregationType:
                type: string
              groups:
                type: object
              all:
                type: integer
            type: object
        pagination:
          $ref: '#/components/schemas/Pagination'
      type: object
    DatasetV1:
      title: 'Dataset (v1)'
      properties:
        name:
          description: 'Machine-readable dataset identifier, used as the datasetName path parameter in other Datasets endpoints.'
          type: string
          example: employee
        label:
          description: 'Human-readable display name for the dataset.'
          type: string
          example: Employee
      type: object
    DatasetResponseV1:
      title: 'Dataset Response (v1)'
      properties:
        datasets:
          description: 'Array of available datasets.'
          type: array
          items:
            $ref: '#/components/schemas/DatasetV1'
      type: object
    Field:
      title: Field
      properties:
        name:
          description: 'Machine-readable field identifier. Use this value in the `fields` array when querying data.'
          type: string
          example: status
        label:
          description: 'Human-readable display name for the field.'
          type: string
          example: Status
        parentType:
          description: 'The type of the parent section (e.g. `page`, `table`).'
          type: string
          example: page
        parentName:
          description: 'The name of the parent section this field belongs to.'
          type: string
          example: Personal
        entityName:
          description: 'The entity name for the field, used in `showHistory` when querying historical table fields.'
          type: string
          example: personal
      type: object
    DatasetFieldsResponse:
      title: 'Dataset Fields Response'
      properties:
        pagination:
          $ref: '#/components/schemas/Pagination'
        name:
          description: 'Machine-readable dataset identifier matching the requested datasetName.'
          type: string
          example: employee
        label:
          description: 'Human-readable display name for the dataset.'
          type: string
          example: Employee
        fields:
          description: 'Paginated array of field descriptors for this dataset.'
          type: array
          items:
            $ref: '#/components/schemas/Field'
          example:
            -
              name: status
              label: Status
              parentType: page
              parentName: Personal
              entityName: personal
            -
              name: employeeNumber
              label: 'Employee #'
              parentType: page
              parentName: Personal
              entityName: personal
      type: object
    Report:
      title: 'Report (v1)'
      properties:
        id:
          type: integer
          example: 4
        name:
          type: string
          example: New
      type: object
    ReportsResponse:
      title: 'Reports Response (v1)'
      properties:
        reports:
          type: array
          items:
            $ref: '#/components/schemas/Report'
        pagination:
          $ref: '#/components/schemas/Pagination'
      type: object
    FieldOptionsTransformer:
      title: 'Field Option'
      properties:
        id:
          description: 'The ID of the field option. May be a string or an integer depending on the field type.'
          oneOf:
            -
              type: string
            -
              type: integer
        value:
          description: 'The human-readable label of the field option'
          type: string
      type: object
    TableName:
      description: 'The API name of an employee table. Standard table names: `jobInfo`, `jobInformation`, `compensation`, `employmentStatus`, `contacts`, `emergencyContacts`, `dependents`, `earnings`, `bonus`, `commission`, `benefit_class`, `employeeVisas`, `employeeEducation`, `employeePassports`, `employeeDriverLicenses`, `employeeCertifications`, `employeeStockOptions`, `employeeAssets`, `employeeCreditCards`, `employeeCovidTests`, `employeeCovidVaccinations`, `employeeCovidVaccinationExemptions`, `employeeCovidExposures`, `employeeEquityGrants`, `levelsAndBands`, `employeeProjectPayRates`. Custom company-defined tables also accepted — they use a numeric suffix (e.g., `custom1`, `custom42`). Use the `list-tabular-fields` tool (GET /api/v1/meta/tables) to discover custom table names for a specific company.'
      type: string
      example: employeeEducation
    TableRowDeleteResponse:
      description: 'Response from deleting an employee table row.'
      properties:
        success:
          description: 'Whether the row was successfully deleted.'
          type: boolean
        error:
          description: 'Error message when success is false. Absent when success is true.'
          type: string
      type: object
    ErrorResponse:
      description: 'Standard error response from BaseController'
      properties:
        error:
          properties:
            code:
              type: integer
              example: 500
            message:
              type: string
              example: 'Internal server error'
          type: object
      type: object
    GoalFiltersV1:
      description: 'Array containing goal filters and their counts for API v1'
      properties:
        filters:
          description: 'Array of goal filter objects with counts'
          type: array
          items:
            properties:
              id:
                description: 'Filter identifier based on goal status.'
                type: string
                enum:
                  - status-inProgress
                  - status-completed
                example: status-inProgress
              name:
                description: 'Display name of the filter'
                type: string
                enum:
                  - 'In Progress'
                  - Completed
                example: 'In Progress'
              count:
                description: 'Number of goals matching this filter'
                type: integer
                example: 1
            type: object
      type: object
    GoalFiltersV1_1:
      description: 'Array containing goal filters, their counts, and actions'
      properties:
        filters:
          description: 'Array of goal filter objects with counts'
          type: array
          items:
            properties:
              id:
                description: 'Filter identifier based on goal status.'
                type: string
                enum:
                  - status-inProgress
                  - status-completed
                  - status-closed
                example: status-inProgress
              name:
                description: 'Display name of the filter'
                type: string
                enum:
                  - 'In Progress'
                  - Completed
                  - Closed
                example: 'In Progress'
              count:
                description: 'Number of goals matching this filter'
                type: integer
                example: 1
              actions:
                description: 'Available actions for goals in this state, defined by PerformanceDefinitions::GOAL_FILTER_ACTIONS'
                properties:
                  canCloseGoal: { description: 'Whether goals in this state can be closed', type: boolean, example: true }
                  canEditGoal: { description: 'Whether goals in this state can be edited', type: boolean, example: true }
                  canEditGoalProgressBar: { description: 'Whether progress bar can be edited for goals in this state', type: boolean, example: true }
                  canReopenGoal: { description: 'Whether goals in this state can be reopened', type: boolean, example: false }
                  canShareGoal: { description: 'Whether goals in this state can be shared', type: boolean, example: true }
                type: object
            type: object
      type: object
    TransformedApiGoal:
      properties:
        id:
          description: 'The id of the goal.'
          type: string
          example: '4'
        title:
          description: 'Title of the goal.'
          type: string
          example: 'Complete Documentation Epic'
        description:
          description: 'A description of the goal.'
          type: string
          example: 'Document every endpoint for the goals API.'
        percentComplete:
          description: 'A percentage (0-100) that denotes how complete the goal is.'
          type: integer
          example: 78
        alignsWithOptionId:
          description: 'The ID of the goal this goal is aligned with. Returns null when not aligned, or an empty string when the goal has no parent set.'
          oneOf:
            -
              type: string
            -
              type: 'null'
        sharedWithEmployeeIds:
          description: 'Ids of the employees that have access to this goal.'
          type: array
          items:
            type: integer
        dueDate:
          description: 'The due date of the goal.'
          type: string
          example: '2025-12-31'
        completionDate:
          description: 'ISO 8601 UTC timestamp of when the goal was completed.'
          example: '2025-12-17T10:30:00Z'
          oneOf:
            -
              type: string
            -
              type: 'null'
        lastChangedDateTime:
          description: 'ISO 8601 UTC timestamp of when the goal was last modified.'
          example: '2025-12-17T10:30:00Z'
          oneOf:
            -
              type: string
            -
              type: 'null'
        status:
          description: 'The status of the goal.'
          type: string
          enum:
            - in_progress
            - completed
            - closed
          example: in_progress
        milestones:
          description: 'All milestones for the individual goal. This array will not exist if milestones are not selected for this goal.'
          oneOf:
            -
              type: array
              items:
                properties:
                  id: { description: 'The id of the milestone.', type: integer, example: 2 }
                  employeeGoalId: { description: 'The id of the goal which encompasses this milestone.', type: integer, example: 12 }
                  title: { description: 'The title of the milestone.', type: string, example: 'Sell 15 Doohickeys' }
                  currentValue: { description: 'The current value for a numeric milestone. This number will be rounded to the nearest hundredth. On the creation of a numeric milestone this value will automatically be set to the start value of the milestone. If the milestone is a simple checkbox milestone, this value will always be null.', example: '9.9', deprecated: true, oneOf: [{ type: number }, { type: 'null' }] }
                  startValue: { description: 'The starting value for a numeric milestone. This number will be rounded to the nearest hundredth. If the milestone is a simple checkbox milestone, this value will always be null.', example: '7.01', deprecated: true, oneOf: [{ type: number }, { type: 'null' }] }
                  endValue: { description: 'The end goal for a numeric milestone. This number will be rounded to the nearest hundredth. If the milestone is a simple checkbox milestone, this value will always be null.', example: '15', deprecated: true, oneOf: [{ type: number }, { type: 'null' }] }
                  completedDateTime: { description: 'The date and time in which the goal has been completed. If the goal is not completed the value will be null.', type: string, example: '2025-04-10T15:36:38Z' }
                  lastUpdateDateTime: { description: 'The date and time in which the goal was last updated.', type: string, example: '2025-04-10T15:36:38Z' }
                  lastUpdateUserId: { description: 'The ID of the user who last updated this milestone.', type: integer, example: 24 }
                type: object
            -
              type: 'null'
        actions:
          description: 'Actions that are available to a goal with milestones enabled. This object will not appear on a goal without milestones.'
          properties:
            canEditGoalProgressBar:
              description: 'Can the user edit the progress bar of this goal.'
              type: boolean
              example: false
            canEditGoalMilestoneProgressBar:
              description: 'can the user edit the progress of a milestone in this goal.'
              type: boolean
              example: true
          oneOf:
            -
              type: object
            -
              type: 'null'
      type: object
    TransformedApiEmployeeGoalDetails:
      properties:
        goal:
          description: 'An individual goal'
          properties:
            id:
              description: 'The id of the goal.'
              type: string
              example: '4'
            title:
              description: 'Title of the goal.'
              type: string
              example: 'Complete Documentation Epic'
            description:
              description: 'A description of the goal.'
              type: string
              example: 'Document every endpoint for the goals API.'
            percentComplete:
              description: 'A percentage (0-100) that denotes how complete the goal is.'
              type: integer
              example: 78
            alignsWithOptionId:
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            sharedWithEmployeeIds:
              description: 'Ids of the employees that have access to this goal.'
              type: array
              items:
                type: integer
            dueDate:
              description: 'The due date of the goal.'
              type: string
              example: '2025-12-31'
            completionDate:
              description: 'ISO 8601 UTC timestamp of when the goal was completed.'
              example: '2025-12-17T10:30:00Z'
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            lastChangedDateTime:
              description: 'ISO 8601 UTC timestamp of when the goal was last modified.'
              example: '2025-12-17T10:30:00Z'
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            status:
              description: 'The status of the goal.'
              type: string
              enum:
                - in_progress
                - completed
                - closed
              example: in_progress
            milestones:
              description: 'All milestones for the individual goal. This array will not exist if milestones are not selected for this goal.'
              oneOf:
                -
                  type: array
                  items: { properties: { id: { description: 'The id of the milestone.', type: integer, example: 2 }, employeeGoalId: { description: 'The id of the goal which encompasses this milestone.', type: integer, example: 12 }, title: { description: 'The title of the milestone.', type: string, example: 'Sell 15 Doohickeys' }, currentValue: { description: 'The current value for a numeric milestone. This number will be rounded to the nearest hundredth. On the creation of a numeric milestone this value will automatically be set to the start value of the milestone. If the milestone is a simple checkbox milestone, this value will always be null.', example: '9.9', deprecated: true, oneOf: [{ type: number }, { type: 'null' }] }, startValue: { description: 'The starting value for a numeric milestone. This number will be rounded to the nearest hundredth. If the milestone is a simple checkbox milestone, this value will always be null.', example: '7.01', deprecated: true, oneOf: [{ type: number }, { type: 'null' }] }, endValue: { description: 'The end goal for a numeric milestone. This number will be rounded to the nearest hundredth. If the milestone is a simple checkbox milestone, this value will always be null.', example: '15', deprecated: true, oneOf: [{ type: number }, { type: 'null' }] }, completedDateTime: { description: 'The date and time in which the goal has been completed. If the goal is not completed the value will be null.', type: string, example: '2025-04-10T15:36:38Z' }, lastUpdateDateTime: { description: 'The date and time in which the goal was last updated.', type: string, example: '2025-04-10T15:36:38Z' }, lastUpdateUserId: { description: 'The ID of the user who last updated this milestone.', type: integer, example: 24 } }, type: object }
                -
                  type: 'null'
            actions:
              description: 'Actions that are available to a goal with milestones enabled. This object will not appear on a goal without milestones.'
              properties:
                canEditGoalProgressBar:
                  description: 'Can the user edit the progress bar of this goal.'
                  type: boolean
                  example: true
                canEditGoalMilestoneProgressBar:
                  description: 'can the user edit the progress of a milestone in this goal.'
                  type: boolean
                  example: false
              oneOf:
                -
                  type: object
                -
                  type: 'null'
          type: object
      type: object
    ProjectInfoApiTransformer:
      description: 'Project information data representation'
      properties:
        project:
          description: 'Project information'
          properties:
            id:
              description: 'Project ID'
              type: integer
            name:
              description: 'Project name'
              type: string
          type: object
        task:
          description: 'Task information'
          properties:
            id:
              description: 'Task ID'
              type: integer
            name:
              description: 'Task name'
              type: string
          oneOf:
            -
              type: object
            -
              type: 'null'
      type: object
    TotalRewardsOnboardingStep:
      properties:
        name:
          description: 'Name of the onboarding step'
          type: string
        completed:
          description: 'Whether or not the onboarding step is completed'
          type: boolean
      type: object
    CompanyIntegrationsUpdatedWebhookPayload:
      title: 'Company Integrations Updated Webhook Payload'
      description: 'Webhook payload sent when company integrations are updated.'
      properties:
        type:
          description: 'The event type identifier'
          type: string
          enum:
            - company-integrations.updated
          example: company-integrations.updated
        timestamp:
          description: 'ISO 8601 timestamp (UTC) when the event was fired'
          type: string
          format: date-time
          example: '2025-02-18T15:30:00Z'
        data:
          description: 'Event data containing company information'
          properties:
            companyId:
              description: 'The company ID'
              type: string
              example: '12345'
          type: object
      type: object
    CompanyUpdatedWebhookPayload:
      title: 'Company Updated Webhook Payload'
      description: 'Webhook payload sent when a company is updated.'
      properties:
        type:
          description: 'The event type identifier'
          type: string
          enum:
            - company.updated
          example: company.updated
        timestamp:
          description: 'ISO 8601 timestamp (UTC) when the event was fired'
          type: string
          format: date-time
          example: '2025-02-18T15:30:00Z'
        data:
          description: 'Event data containing company information'
          properties:
            companyId:
              description: 'The company ID'
              type: string
              example: '12345'
          type: object
      type: object
    CompanyDeletedWebhookPayload:
      title: 'Company Deleted Webhook Payload'
      description: 'Webhook payload sent when a company is deleted.'
      properties:
        type:
          description: 'The event type identifier'
          type: string
          enum:
            - company.deleted
          example: company.deleted
        timestamp:
          description: 'ISO 8601 timestamp (UTC) when the event was fired'
          type: string
          format: date-time
          example: '2025-02-18T15:30:00Z'
        data:
          description: 'Event data containing company information'
          properties:
            companyId:
              description: 'The company ID'
              type: string
              example: '12345'
          type: object
      type: object
    EmployeeCreatedWebhookPayload:
      title: 'Employee Created Webhook Payload'
      description: 'Webhook payload sent when a new employee is created.'
      properties:
        type:
          description: 'The event type identifier'
          type: string
          enum:
            - employee.created
          example: employee.created
        timestamp:
          description: 'ISO 8601 timestamp (UTC) when the event was fired'
          type: string
          format: date-time
          example: '2025-02-18T15:30:00Z'
        data:
          description: 'Event data containing employee information'
          properties:
            companyId:
              description: 'The company ID'
              type: string
              example: '12345'
            employeeId:
              description: 'The employee ID that was created'
              type: string
              example: '100'
          type: object
      type: object
    EmployeeDeletedWebhookPayload:
      title: 'Employee Deleted Webhook Payload'
      description: 'Webhook payload sent when an employee is deleted.'
      properties:
        type:
          description: 'The event type identifier'
          type: string
          enum:
            - employee.deleted
          example: employee.deleted
        timestamp:
          description: 'ISO 8601 timestamp (UTC) when the event was fired'
          type: string
          format: date-time
          example: '2025-02-18T15:30:00Z'
        data:
          description: 'Event data containing employee information'
          properties:
            companyId:
              description: 'The company ID'
              type: string
              example: '12345'
            employeeId:
              description: 'The employee ID that was deleted'
              type: string
              example: '100'
          type: object
      type: object
    EmployeeUpdatedWebhookPayload:
      title: 'Employee Updated Webhook Payload'
      description: 'Webhook payload sent when an employee is updated.'
      properties:
        type:
          description: 'The event type identifier'
          type: string
          enum:
            - employee.updated
          example: employee.updated
        timestamp:
          description: 'ISO 8601 timestamp (UTC) when the event was fired'
          type: string
          format: date-time
          example: '2025-02-18T15:30:00Z'
        data:
          description: 'Event data containing employee and change information'
          properties:
            companyId:
              description: 'The company ID'
              type: string
              example: '12345'
            employeeId:
              description: 'The employee ID that was updated'
              type: string
              example: '100'
            changedFields:
              description: 'Array of API aliases for fields that changed and are monitored by this webhook'
              type: array
              items:
                type: string
              example:
                - firstName
                - lastName
                - department
          type: object
      type: object
    CompanyAlertDataObject:
      properties:
        id:
          description: 'The unique identifier of the company alert.'
          type: integer
        bambooAlertId:
          description: 'The unique identifier of the Bamboo alert.'
          type: integer
        schedule:
          description: 'The schedule for the company alert.'
          type: string
          enum:
            - daily
            - weekly
            - monthly
            - quarterly
        dueWithin:
          description: 'The number of days before the alert is due.'
          oneOf:
            -
              type: integer
            -
              type: 'null'
        dueInterval:
          description: 'The interval for the alert.'
          oneOf:
            -
              type: string
            -
              type: 'null'
        sendToEmployee:
          description: 'Whether the alert should be sent to employees.'
          type: boolean
        sendToManager:
          description: 'Whether the alert should be sent to managers.'
          type: boolean
        sendToAdmin:
          description: 'Whether the alert should be sent to admins.'
          type: boolean
        editorUserId:
          description: 'The user ID of the editor.'
          oneOf:
            -
              type: integer
            -
              type: 'null'
        lastEdited:
          description: 'The last edited date of the company alert.'
          type:
            - 'null'
        customMessage:
          description: 'The custom message for the company alert.'
          oneOf:
            -
              type: string
            -
              type: 'null'
        customSubject:
          description: 'The custom subject for the company alert.'
          oneOf:
            -
              type: string
            -
              type: 'null'
        groupBy:
          description: 'The group by for the company alert.'
          oneOf:
            -
              type: string
            -
              type: 'null'
        limitTrainingToRequired:
          description: 'Whether the training should be limited to required training.'
          type: boolean
        runAtTime:
          description: 'The time the company alert should run at.'
          oneOf:
            -
              type: string
            -
              type: 'null'
        runAtTimeZone:
          description: 'The time zone the company alert should run at.'
          oneOf:
            -
              type: string
            -
              type: 'null'
        includePosition:
          description: 'Whether the alert should include position.'
          type: boolean
        includeLocation:
          description: 'Whether the alert should include location.'
          type: boolean
        additionalRecipientEmails:
          description: 'The additional recipient emails for the company alert.'
          type: array
          items:
            type: string
        employeeIds:
          description: 'The employee IDs for the company alert.'
          type: array
          items:
            type: string
        listValueIds:
          description: 'The list value IDs for the company alert.'
          type: array
          items:
            type: integer
        filterListValueIds:
          description: 'The filter list value IDs for the company alert.'
          type: array
          items:
            type: integer
        userIds:
          description: 'The user IDs for the company alert.'
          type: array
          items:
            type: integer
      type: object
    TimezoneResource:
      description: 'A timezone resource.'
      properties:
        id:
          description: 'The timezone ID.'
          type: string
          example: '1'
        gmtOffset:
          description: 'The GMT offset label (e.g. "(GMT-07:00)").'
          type: string
          example: '(GMT-07:00)'
        name:
          description: 'The display name of the timezone.'
          type: string
          example: 'Mountain Time'
        utcName:
          description: 'The IANA/UTC timezone identifier.'
          type: string
          example: America/Denver
      type: object
    CountriesResponseSchema:
      description: 'Top-level JSON array of country objects. The response is not wrapped in a parent object.'
      type: array
      items:
        $ref: '#/components/schemas/CountrySchema'
    CountrySchema:
      description: 'A country in the list returned by the Get Countries endpoint'
      properties:
        id:
          description: 'Internal country identifier. Use the same value as the `countryId` path parameter for Get States by Country ID (this endpoint returns JSON numbers, not stringified digits).'
          type: integer
        name:
          description: 'Full display name of the country'
          type: string
        isoCode:
          description: 'ISO 3166-1 alpha-2 when defined. The `isoCode` key is always present; the value is JSON `null` when the country has no code set in data (not omitted).'
          oneOf:
            -
              type: string
            -
              type: 'null'
      type: object
    StateProvinceResponseSchema:
      description: 'Response from `GET /api/v1/meta/provinces/{countryId}`. The `options` list is sorted by `label` (subdivision abbreviation).'
      properties:
        options:
          description: 'Subdivisions for the requested `countryId`. Each object matches StateProvinceSchema; `label` is the subdivision abbreviation, `name` the full name, `iso` the ISO 3166-2 code.'
          type: array
          items:
            $ref: '#/components/schemas/StateProvinceSchema'
      type: object
    StateProvinceSchema:
      description: 'One state or province (subdivision) row. The same field meanings as each element of `StateProvinceResponseSchema.options` from `GET /api/v1/meta/provinces/{countryId}`.'
      properties:
        id:
          description: 'State/province ID'
          type: integer
        label:
          description: 'Subdivision abbreviation (e.g. "UT" for Utah). This is the short code used in `options[].label`, not the full name (see `name`).'
          type: string
        iso:
          description: 'ISO 3166-2 subdivision code (e.g. "US-UT" for Utah).'
          type: string
        name:
          description: 'Full subdivision name (e.g. "Utah"), distinct from the abbreviation in `label`.'
          type: string
      type: object
    CompanyIndustryCollection:
      title: CompanyIndustryDataObjectCollection
      type: array
      items:
        $ref: '#/components/schemas/CompanyIndustryDataObject'
    CompanyIndustryDataObject:
      description: "Company's enabled industry."
      required:
        - industryId
        - addedByCustomer
        - addedByUserId
        - addedYmdt
      properties:
        industryId:
          description: "The company's industry id."
          type: integer
        addedByCustomer:
          description: 'If the industry was added by the customer.'
          type: boolean
        addedByUserId:
          description: 'The user id of the user who added the industry.'
          oneOf:
            -
              type: integer
            -
              type: 'null'
        addedYmdt:
          description: 'The date that the industry was added.'
          oneOf:
            -
              type: string
            -
              type: 'null'
      type: object
    CompensationBenchmarking-ColumnMap:
      description: 'Schema for compensation benchmarking column map'
      properties:
        expectedColumnKey:
          description: 'Expected column key'
          oneOf:
            -
              type: string
            -
              type: 'null'
        columnName:
          description: 'Column name'
          type: string
      type: object
    BudgetGuidelinesView:
      description: 'Budget guidelines for compensation planning budgets'
      properties:
        useGuidelines:
          description: 'Whether to use budget guidelines'
          type: boolean
        softMinimum:
          description: 'Soft minimum budget guideline'
          oneOf:
            -
              type: number
            -
              type: 'null'
        softMaximum:
          description: 'Soft maximum budget guideline'
          oneOf:
            -
              type: number
            -
              type: 'null'
        guidelinesWarningErrors:
          oneOf:
            -
              $ref: '#/components/schemas/BudgetGuidelinesWarnings'
              description: 'Budget guidelines warning errors'
            -
              type: 'null'
          description: 'Budget guidelines warning errors'
        requireComments:
          description: 'Whether a comment is required for recommendations outside of guidelines'
          type: boolean
      type: object
    BudgetGuidelinesWarnings:
      description: 'Warning messages for saved budget guidelines'
      properties:
        softMinimumWarningMessage:
          description: 'Soft minimum warning message'
          oneOf:
            -
              type: string
            -
              type: 'null'
        softMaximumWarningMessage:
          description: 'Soft maximum warning message'
          oneOf:
            -
              type: string
            -
              type: 'null'
      type: object
    CompensationToolsDataObject:
      description: 'Schema for the compensation tools list.'
      properties:
        levelsAndBands:
          description: 'Has access to Levels & Bands tool'
          type: boolean
        compensationBenchmarking:
          description: 'Has access to Compensation Benchmarking tool'
          type: boolean
        compensationPlanning:
          description: 'Has access to Compensation Planning tool'
          type: boolean
        totalRewards:
          description: 'Has access to Total Rewards tool'
          type: boolean
      type: object
    CompensationUpsellData:
      description: 'Schema for the compensation upsell data.'
      properties:
        accountOwnerName:
          description: 'Account owner name'
          type: string
        canRequestDemo:
          description: 'Whether user can request a demo'
          type: boolean
        userPermissions:
          $ref: '#/components/schemas/UserPermissionData'
          description: 'User permissions'
      type: object
    ConversionRateDataObject:
      title: ConversionRateDataObject
      description: 'Represents a currency conversion rate from a source to a target currency.'
      properties:
        currencyName:
          description: 'The full name of the target currency.'
          type: string
        currencyCode:
          description: 'The code of the target currency (e.g. USD, EUR, JPY).'
          type: string
        conversionRate:
          description: 'The numeric conversion rate to the target currency.'
          type: number
          format: float
      type: object
    DetailsAndCurrencyRequestDataObject:
      title: DetailsAndCurrencyRequestDataObject
      description: 'Request data for updating compensation planning cycle details and currency settings. Pre-condition: User must have permission to update cycle. Post-condition: Cycle configuration is updated.'
      properties:
        id:
          description: 'ID of the cycle to update'
          type: string
        cycleName:
          description: 'Name of the cycle to update'
          type: string
        cycleCurrency:
          description: 'Currency code for the cycle'
          oneOf:
            -
              type: string
            -
              type: 'null'
        cycleWindowStartYmd:
          description: 'Start date of the cycle window'
          type: string
          format: date
        cycleWindowEndYmd:
          description: 'End date of the cycle window'
          type: string
          format: date
        recommendationsDueYmd:
          description: 'Date when recommendations are due'
          type: string
          format: date
        approvalsDueYmd:
          description: 'Date when approvals are due'
          type: string
          format: date
        cycleEffectiveDate:
          description: 'Effective date of the cycle'
          type: string
          format: date
        salaryIncluded:
          description: 'Whether salary is included in this cycle'
          type: boolean
        bonusIncluded:
          description: 'Whether bonus is included in this cycle'
          type: boolean
        equityIncluded:
          description: 'Whether equity is included in this cycle'
          type: boolean
        cycleCurrencyConversionRates:
          description: 'Currency conversion rates'
          type: array
          items:
            $ref: '#/components/schemas/ConversionRateDataObject'
        paycheckDateYmd:
          description: 'Date of first paycheck with new compensation updates'
          type: string
          format: date
      type: object
    UserPermissionData:
      description: 'Schema for the compensation upsell data.'
      properties:
        isAdmin:
          description: 'Whether user is an admin'
          type: boolean
        isBillingContact:
          description: 'Whether user is a billing contact'
          type: boolean
        isOwner:
          description: 'Whether user is the owner'
          type: boolean
        isSupportAdmin:
          description: 'Whether user is a support admin'
          type: boolean
      type: object
    Employee-StringCodeErrorResponse-V1:
      description: 'Error response returned by Employee API endpoints where `error.code` is a string identifier.'
      required:
        - error
      properties:
        error:
          required:
            - code
            - message
          properties:
            code:
              description: 'String error code identifier (for example `BadRequest` or `BadPage`).'
              type: string
              example: BadRequest
            message:
              description: 'Human-readable error message.'
              type: string
              example: 'Invalid request parameters.'
          type: object
      type: object
    EmployeeCursorPaginationQueryObject:
      type: object
      allOf:
        -
          $ref: '#/components/schemas/CursorPaginationQueryObject'
        -
          properties:
            before:
              description: 'Cursor pointing to the start of the previous page. Use the `prevCursor` value from the last response to paginate backward.'
              type: string
            after:
              description: 'Cursor pointing to the start of the next page. Use the `nextCursor` value from the last response to paginate forward.'
              type: string
            limit:
              description: 'Maximum number of items to return. This can be at most 2500.'
              type: integer
              default: 250
              maximum: 2500
              minimum: 1
          type: object
    GetEmployeeResponse:
      description: "Employee data returned by the Get Employee endpoint.\n\tThe `id` field is always present; all other named properties are included only when explicitly requested via the `fields` query parameter.\n\tAdditional custom or company-configured fields may also appear."
      required:
        - id
      properties:
        id:
          description: 'Unique identifier for the employee'
          type: string
        firstName:
          description: "Employee's first name"
          oneOf:
            -
              type: string
            -
              type: 'null'
        lastName:
          description: "Employee's last name"
          oneOf:
            -
              type: string
            -
              type: 'null'
        preferredName:
          description: "Employee's preferred name"
          oneOf:
            -
              type: string
            -
              type: 'null'
        middleName:
          description: "Employee's middle name"
          oneOf:
            -
              type: string
            -
              type: 'null'
        photoUrl:
          description: "URL to the employee's profile photo"
          oneOf:
            -
              type: string
            -
              type: 'null'
        jobTitleName:
          description: "Employee's current job title"
          oneOf:
            -
              type: string
            -
              type: 'null'
        jobTitleId:
          description: "Employee's job title ID"
          oneOf:
            -
              type: string
            -
              type: 'null'
        status:
          description: "Employee's current status"
          oneOf:
            -
              type: string
              enum:
                - Active
                - Inactive
            -
              type: 'null'
        workEmail:
          description: "Employee's work email address"
          oneOf:
            -
              type: string
            -
              type: 'null'
        homeEmail:
          description: "Employee's home email address"
          oneOf:
            -
              type: string
            -
              type: 'null'
        bestEmail:
          description: "Employee's best email address"
          oneOf:
            -
              type: string
            -
              type: 'null'
        workPhone:
          description: "Employee's work phone number"
          oneOf:
            -
              type: string
            -
              type: 'null'
        workPhoneExtension:
          description: "Employee's work phone extension"
          oneOf:
            -
              type: string
            -
              type: 'null'
        mobilePhone:
          description: "Employee's mobile phone number"
          oneOf:
            -
              type: string
            -
              type: 'null'
        homePhone:
          description: "Employee's home phone number"
          oneOf:
            -
              type: string
            -
              type: 'null'
        skypeUsername:
          description: "Employee's Skype username"
          oneOf:
            -
              type: string
            -
              type: 'null'
        linkedinUrl:
          description: "Employee's LinkedIn profile URL"
          oneOf:
            -
              type: string
            -
              type: 'null'
        facebookUrl:
          description: "Employee's Facebook profile URL"
          oneOf:
            -
              type: string
            -
              type: 'null'
        instagramUrl:
          description: "Employee's Instagram profile URL"
          oneOf:
            -
              type: string
            -
              type: 'null'
        twitterUrl:
          description: "Employee's Twitter/X profile URL"
          oneOf:
            -
              type: string
            -
              type: 'null'
        pinterestUrl:
          description: "Employee's Pinterest profile URL"
          oneOf:
            -
              type: string
            -
              type: 'null'
        birthDate:
          description: "Employee's birth date"
          oneOf:
            -
              type: string
            -
              type: 'null'
        hireDate:
          description: "Employee's hire date"
          oneOf:
            -
              type: string
            -
              type: 'null'
        originalHireDate:
          description: "Employee's original hire date"
          oneOf:
            -
              type: string
            -
              type: 'null'
        terminationDate:
          description: "Employee's termination date"
          oneOf:
            -
              type: string
            -
              type: 'null'
        address1:
          description: "Employee's street address"
          oneOf:
            -
              type: string
            -
              type: 'null'
        city:
          description: "Employee's city"
          oneOf:
            -
              type: string
            -
              type: 'null'
        state:
          description: "Employee's state or province"
          oneOf:
            -
              type: string
            -
              type: 'null'
        country:
          description: "Employee's country"
          oneOf:
            -
              type: string
            -
              type: 'null'
        gender:
          description: "Employee's gender"
          oneOf:
            -
              type: string
            -
              type: 'null'
        marital:
          description: "Employee's marital status"
          oneOf:
            -
              type: string
            -
              type: 'null'
        payRate:
          description: "Employee's pay rate"
          oneOf:
            -
              type: string
            -
              type: 'null'
        payType:
          description: "Employee's pay type"
          oneOf:
            -
              type: string
            -
              type: 'null'
        payPeriod:
          description: "Employee's pay period"
          oneOf:
            -
              type: string
            -
              type: 'null'
        exempt:
          description: 'Whether the employee is FLSA exempt'
          oneOf:
            -
              type: string
            -
              type: 'null'
        canUploadPhoto:
          description: 'Whether the requesting user can upload a photo for this employee'
          oneOf:
            -
              type: boolean
            -
              type: 'null'
        division:
          description: "Employee's division name"
          oneOf:
            -
              type: string
            -
              type: 'null'
        divisionId:
          description: "Employee's division ID"
          oneOf:
            -
              type: string
            -
              type: 'null'
        department:
          description: "Employee's department name"
          oneOf:
            -
              type: string
            -
              type: 'null'
        departmentId:
          description: "Employee's department ID"
          oneOf:
            -
              type: string
            -
              type: 'null'
        location:
          description: "Employee's location name"
          oneOf:
            -
              type: string
            -
              type: 'null'
        locationId:
          description: "Employee's location ID"
          oneOf:
            -
              type: string
            -
              type: 'null'
        employmentStatus:
          description: "Employee's current employment status name"
          oneOf:
            -
              type: string
            -
              type: 'null'
        employmentStatusId:
          description: "Employee's current employment status ID"
          oneOf:
            -
              type: string
            -
              type: 'null'
        reportsToName:
          description: "Name of the employee's manager"
          oneOf:
            -
              type: string
            -
              type: 'null'
        reportsToId:
          description: "Employee ID of the employee's manager"
          oneOf:
            -
              type: string
            -
              type: 'null'
      type: object
      additionalProperties: true
    GetEmployeesEmployeeBaseResponse:
      description: 'Base employee fields that are always included in the response.'
      required:
        - employeeId
        - firstName
        - lastName
        - preferredName
        - photoUrl
        - jobTitleName
        - status
        - _restrictedFields
      properties:
        employeeId:
          description: 'Unique identifier for the employee'
          type: string
        firstName:
          description: "Employee's first name"
          oneOf:
            -
              type: string
            -
              type: 'null'
        lastName:
          description: "Employee's last name"
          oneOf:
            -
              type: string
            -
              type: 'null'
        preferredName:
          description: "Employee's preferred name"
          oneOf:
            -
              type: string
            -
              type: 'null'
        photoUrl:
          description: "URL to employee's profile photo"
          oneOf:
            -
              type: string
            -
              type: 'null'
        jobTitleName:
          description: "Employee's current job title"
          oneOf:
            -
              type: string
            -
              type: 'null'
        status:
          description: "Employee's current status (Active or Inactive)."
          oneOf:
            -
              type: string
            -
              type: 'null'
        _restrictedFields:
          description: 'Array of field names that are restricted due to permission checks'
          type: array
          items:
            type: string
      type: object
    GetEmployeesEmployeeResponse:
      description: 'Employee data response object containing basic employee information and optionally requested fields. When the `fields` parameter is provided, additional requested fields will be included. Field values are subject to permission checks — restricted fields are returned as `null` and their names are listed in `_restrictedFields`.'
      type: object
      allOf:
        -
          $ref: '#/components/schemas/GetEmployeesEmployeeBaseResponse'
        -
          properties:
            addressLine1:
              description: "Employee's address line 1. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            addressLine2:
              description: "Employee's address line 2. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            age:
              description: "Employee's age. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            allergies:
              description: "Employee's allergies. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            bestEmail:
              description: "Employee's best email address. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            birthDate:
              description: "Employee's birth date. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            birthplace:
              description: "Employee's birthplace. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            citizenship:
              description: "Employee's citizenship. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            citizenshipId:
              description: "Employee's citizenship ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            city:
              description: "Employee's city. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            compensationChangeReason:
              description: "Employee's compensation change reason. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            compensationChangeReasonId:
              description: "Employee's compensation change reason ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            compensationComment:
              description: "Employee's compensation comment. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            compensationEffectiveDate:
              description: "Employee's compensation effective date. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            compensationEndDate:
              description: "Employee's compensation end date. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            contractEndDate:
              description: "Employee's contract end date. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            country:
              description: "Employee's country. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            countryId:
              description: "Employee's country ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            departmentId:
              description: "Employee's department ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            departmentName:
              description: "Employee's department name. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            dietaryRestrictions:
              description: "Employee's dietary restrictions. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            displayName:
              description: "Employee's name displayed in the company configured format. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            divisionId:
              description: "Employee's division ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            divisionName:
              description: "Employee's division name. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            eeoJobCategory:
              description: "Employee's EEO job category. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            eeoJobCategoryId:
              description: "Employee's EEO job category ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            ein:
              description: "Employee's EIN. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            eligibleForRehire:
              description: "Employee's eligible-for-rehire flag. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            eligibleForRehireId:
              description: "Employee's eligible-for-rehire ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            employeeName:
              description: "Employee's name (last, first). Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            employeeNumber:
              description: "Employee's employee number. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            employmentStatusComment:
              description: "Employee's employment status comment. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            employmentStatusEffectiveDate:
              description: "Employee's employment status effective date. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            employmentStatusId:
              description: "Employee's employment status id. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            employmentStatusName:
              description: "Employee's employment status name. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            employmentType:
              description: "Employee's employment type. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            employmentTypeId:
              description: "Employee's employment type ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            ethnicity:
              description: "Employee's ethnicity. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            ethnicityId:
              description: "Employee's ethnicity ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            facebookUrl:
              description: "Employee's Facebook profile URL. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            finalDoseAdministrationDate:
              description: "Employee's final COVID dose administration date. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            finalPayDate:
              description: "Employee's final pay date. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            firstNameLastName:
              description: "Employee's first and last name. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            firstNameMiddleInitial:
              description: "Employee's first name and middle initial. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            flsaCode:
              description: "Employee's FLSA code. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            flsaCodeId:
              description: "Employee's FLSA code ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            gender:
              description: "Employee's gender. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            genderIdentity:
              description: "Employee's gender identity. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            genderIdentityId:
              description: "Employee's gender identity ID. Only included when requested via the `fields` parameter."
              type: array
              items:
                type: integer
            hireDate:
              description: "Employee's hire date. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            homeEmail:
              description: "Employee's home email address. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            homePhone:
              description: "Employee's home phone number. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            hoursPerPayCycle:
              description: "Employee's hours per pay cycle. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            instagramUrl:
              description: "Employee's Instagram profile URL. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            isManager:
              description: 'Whether the employee is a manager. Only included when requested via the `fields` parameter.'
              oneOf:
                -
                  type: boolean
                -
                  type: 'null'
            jacketSize:
              description: "Employee's jacket size. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            jacketSizeId:
              description: "Employee's jacket size ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            jobInformationEffectiveDate:
              description: "Employee's job information effective date. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            jobTitleId:
              description: "Employee's job title id. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            linkedinUrl:
              description: "Employee's LinkedIn profile URL. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            locationId:
              description: "Employee's location ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            locationName:
              description: "Employee's location name. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            maritalStatus:
              description: "Employee's marital status. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            middleInitial:
              description: "Employee's middle initial. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            middleName:
              description: "Employee's middle name. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            mobilePhone:
              description: "Employee's mobile phone number. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            nationalId:
              description: "Employee's national ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            nationalInsuranceCategory:
              description: "Employee's national insurance category. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            nationalInsuranceCategoryId:
              description: "Employee's national insurance category ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            nationality:
              description: "Employee's nationality. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            nationalityId:
              description: "Employee's nationality ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            nickName:
              description: "Employee's preferred name. If preferred name is not set then employee's first name is returned. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            nin:
              description: "Employee's NIN. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            noticePeriod:
              description: "Employee's notice period. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            noticePeriodId:
              description: "Employee's notice period ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            originalHireDate:
              description: "Employee's original hire date. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            overtime:
              description: "Employee's overtime status. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            overtimeRate:
              description: "Employee's overtime rate. Only included when requested via the `fields` parameter."
              properties:
                amount:
                  type: string
                currency:
                  type: string
              type: object
            paidPer:
              description: 'How the employee is paid (e.g., hour, year). Only included when requested via the `fields` parameter.'
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            payRate:
              description: "Employee's pay rate. Only included when requested via the `fields` parameter."
              properties:
                amount:
                  type: string
                currency:
                  type: string
              type: object
            paySchedule:
              description: "Employee's pay schedule. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            payScheduleId:
              description: "Employee's pay schedule ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            payType:
              description: "Employee's pay type. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            pinterestUrl:
              description: "Employee's Pinterest profile URL. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            preferredNameLastName:
              description: "Employee's preferred name and last name. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            probationEndDate:
              description: "Employee's probation end date. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            pronouns:
              description: "Employee's pronouns. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            pronounsId:
              description: "Employee's pronouns ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            proofOfVaccination:
              description: 'Whether proof of vaccination has been provided. Only included when requested via the `fields` parameter.'
              oneOf:
                -
                  type: boolean
                -
                  type: 'null'
            reportsToId:
              description: "Employee's manager's employee id. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            reportsToName:
              description: "Employee's manager's name. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            secondaryLanguage:
              description: "Employee's secondary language. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            shirtSize:
              description: "Employee's shirt size. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            shirtSizeId:
              description: "Employee's shirt size ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            sin:
              description: "Employee's SIN. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            skypeUsername:
              description: "Employee's Skype username. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            ssn:
              description: "Employee's SSN. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            state:
              description: "Employee's state. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            stateId:
              description: "Employee's state ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            taxTypeId:
              description: "Employee's tax type ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            teams:
              description: "Employee's teams. Only included when requested via the `fields` parameter."
              type: array
              items:
                properties:
                  id: { type: integer }
                  label: { type: string }
                type: object
            tenure:
              description: "Employee's tenure. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            terminationDate:
              description: "Employee's termination date. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            terminationReason:
              description: "Employee's termination reason. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            terminationReasonId:
              description: "Employee's termination reason ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            terminationRegrettable:
              description: 'Whether the termination was regrettable. Only included when requested via the `fields` parameter.'
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            terminationRegrettableId:
              description: 'Termination regrettable ID. Only included when requested via the `fields` parameter.'
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            terminationType:
              description: "Employee's termination type. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            terminationTypeId:
              description: "Employee's termination type ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            tShirtSize:
              description: "Employee's t-shirt size. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            tShirtSizeId:
              description: "Employee's t-shirt size ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            twitterUrl:
              description: "Employee's Twitter/X profile URL. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            userId:
              description: "Employee's user ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            vaccinationStatus:
              description: "Employee's COVID vaccination status. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            vaccinationStatusId:
              description: "Employee's COVID vaccination status ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            vaccineReceived:
              description: "Employee's COVID vaccine received. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            vaccineReceivedId:
              description: "Employee's COVID vaccine received ID. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            veteranStatus:
              description: "Employee's veteran status. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            veteranStatusId:
              description: "Employee's veteran status ID. Only included when requested via the `fields` parameter."
              type: array
              items:
                type: integer
            workEmail:
              description: "Employee's work email address. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            workPhone:
              description: "Employee's work phone number. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            workPhoneExtension:
              description: "Employee's work phone extension. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            zipcode:
              description: "Employee's zip code. Only included when requested via the `fields` parameter."
              oneOf:
                -
                  type: string
                -
                  type: 'null'
          type: object
    GetEmployeesEmployeeResponseCollection:
      description: 'Typed collection of employee response objects'
      type: array
      items:
        $ref: '#/components/schemas/GetEmployeesEmployeeResponse'
    GetEmployeesFilterRequestObject:
      description: "Filter criteria for the employee list endpoint, supplied as `filter[fieldName]=value` query parameters. All filter fields are optional, and multiple fields are combined with AND logic.\n\nThree matching strategies are used depending on the field:\n\n- **Substring** (case-insensitive `LIKE`) — applied to free-text fields such as names, addresses, social URLs, and free-form labels.\n- **Equality** — applied to IDs (integer), enumerated string values, booleans (`true`/`false`), numeric values, and ISO 8601 dates (`YYYY-MM-DD`).\n- **Special** — `ids` accepts a list of employee IDs (`filter[ids][]=123&filter[ids][]=124` or `filter[ids]=123,124`).\n\nFiltering is **not supported** for the following fields and will return a 422: `bestEmail`, `ein`, `genderIdentity`, `genderIdentityId`, `nationalId`, `nin`, `overtime`, `overtimeRate`, `payRate`, `sin`, `ssn`, `teams`, `userId`, `veteranStatus`, `veteranStatusId`. Any unrecognized filter key will also return a 422."
      required: []
      properties:
        addressLine1:
          description: 'Substring match (case-insensitive).'
          type: string
        addressLine2:
          description: 'Substring match (case-insensitive).'
          type: string
        allergies:
          description: 'Substring match (case-insensitive).'
          type: string
        birthplace:
          description: 'Substring match (case-insensitive).'
          type: string
        citizenship:
          description: 'Substring match (case-insensitive).'
          type: string
        city:
          description: 'Substring match (case-insensitive).'
          type: string
        compensationChangeReason:
          description: 'Substring match (case-insensitive).'
          type: string
        compensationComment:
          description: 'Substring match (case-insensitive).'
          type: string
        country:
          description: 'Substring match (case-insensitive).'
          type: string
        departmentName:
          description: 'Substring match (case-insensitive).'
          type: string
        dietaryRestrictions:
          description: 'Substring match (case-insensitive).'
          type: string
        displayName:
          description: 'Substring match (case-insensitive).'
          type: string
        divisionName:
          description: 'Substring match (case-insensitive).'
          type: string
        eeoJobCategory:
          description: 'Substring match (case-insensitive).'
          type: string
        eligibleForRehire:
          description: 'Substring match (case-insensitive).'
          type: string
        employeeName:
          description: 'Substring match (case-insensitive).'
          type: string
        employeeNumber:
          description: 'Substring match (case-insensitive).'
          type: string
        employmentStatusName:
          description: 'Substring match (case-insensitive).'
          type: string
        employmentStatusComment:
          description: 'Substring match (case-insensitive).'
          type: string
        employmentType:
          description: 'Substring match (case-insensitive).'
          type: string
        ethnicity:
          description: 'Substring match (case-insensitive).'
          type: string
        facebookUrl:
          description: 'Substring match (case-insensitive).'
          type: string
        firstNameLastName:
          description: 'Substring match (case-insensitive).'
          type: string
        firstNameMiddleInitial:
          description: 'Substring match (case-insensitive).'
          type: string
        flsaCode:
          description: 'Substring match (case-insensitive).'
          type: string
        homeEmail:
          description: 'Substring match (case-insensitive).'
          type: string
        homePhone:
          description: 'Substring match (case-insensitive).'
          type: string
        instagramUrl:
          description: 'Substring match (case-insensitive).'
          type: string
        jacketSize:
          description: 'Substring match (case-insensitive).'
          type: string
        linkedinUrl:
          description: 'Substring match (case-insensitive).'
          type: string
        locationName:
          description: 'Substring match (case-insensitive).'
          type: string
        maritalStatus:
          description: 'Substring match (case-insensitive).'
          type: string
        middleInitial:
          description: 'Substring match (case-insensitive).'
          type: string
        middleName:
          description: 'Substring match (case-insensitive).'
          type: string
        mobilePhone:
          description: 'Substring match (case-insensitive).'
          type: string
        nationalInsuranceCategory:
          description: 'Substring match (case-insensitive).'
          type: string
        nationality:
          description: 'Substring match (case-insensitive).'
          type: string
        nickName:
          description: 'Substring match (case-insensitive).'
          type: string
        noticePeriod:
          description: 'Substring match (case-insensitive).'
          type: string
        paySchedule:
          description: 'Substring match (case-insensitive).'
          type: string
        pinterestUrl:
          description: 'Substring match (case-insensitive).'
          type: string
        preferredNameLastName:
          description: 'Substring match (case-insensitive).'
          type: string
        pronouns:
          description: 'Substring match (case-insensitive).'
          type: string
        reportsToName:
          description: 'Substring match (case-insensitive).'
          type: string
        secondaryLanguage:
          description: 'Substring match (case-insensitive).'
          type: string
        shirtSize:
          description: 'Substring match (case-insensitive).'
          type: string
        skypeUsername:
          description: 'Substring match (case-insensitive).'
          type: string
        state:
          description: 'Substring match (case-insensitive).'
          type: string
        terminationReason:
          description: 'Substring match (case-insensitive).'
          type: string
        terminationRegrettable:
          description: 'Substring match (case-insensitive).'
          type: string
        terminationType:
          description: 'Substring match (case-insensitive).'
          type: string
        tShirtSize:
          description: 'Substring match (case-insensitive).'
          type: string
        twitterUrl:
          description: 'Substring match (case-insensitive).'
          type: string
        vaccinationStatus:
          description: 'Substring match (case-insensitive).'
          type: string
        vaccineReceived:
          description: 'Substring match (case-insensitive).'
          type: string
        workEmail:
          description: 'Substring match (case-insensitive).'
          type: string
        workPhone:
          description: 'Substring match (case-insensitive).'
          type: string
        workPhoneExtension:
          description: 'Substring match (case-insensitive).'
          type: string
        zipcode:
          description: 'Substring match (case-insensitive).'
          type: string
        gender:
          description: 'Exact match.'
          type: string
          enum:
            - Male
            - Female
            - Non-Binary
        paidPer:
          description: 'Exact match.'
          type: string
          enum:
            - Day
            - Week
            - Month
            - Quarter
            - Year
            - PayPeriod
            - Piece
        payType:
          description: 'Exact match.'
          type: string
          enum:
            - Hourly
            - Salary
            - Commission
        citizenshipId:
          description: 'Exact match against an integer ID.'
          type: integer
        compensationChangeReasonId:
          description: 'Exact match against an integer ID.'
          type: integer
        countryId:
          description: 'Exact match against an integer ID.'
          type: integer
        departmentId:
          description: 'Exact match against an integer ID.'
          type: integer
        divisionId:
          description: 'Exact match against an integer ID.'
          type: integer
        eeoJobCategoryId:
          description: 'Exact match against an integer ID.'
          type: integer
        eligibleForRehireId:
          description: 'Exact match against an integer ID.'
          type: integer
        employmentStatusId:
          description: 'Exact match against an integer ID.'
          type: integer
        employmentTypeId:
          description: 'Exact match against an integer ID.'
          type: integer
        ethnicityId:
          description: 'Exact match against an integer ID.'
          type: integer
        flsaCodeId:
          description: 'Exact match against an integer ID.'
          type: integer
        jacketSizeId:
          description: 'Exact match against an integer ID.'
          type: integer
        jobTitleId:
          description: 'Exact match against an integer ID.'
          type: integer
        locationId:
          description: 'Exact match against an integer ID.'
          type: integer
        nationalInsuranceCategoryId:
          description: 'Exact match against an integer ID.'
          type: integer
        nationalityId:
          description: 'Exact match against an integer ID.'
          type: integer
        noticePeriodId:
          description: 'Exact match against an integer ID.'
          type: integer
        payScheduleId:
          description: 'Exact match against an integer ID.'
          type: integer
        pronounsId:
          description: 'Exact match against an integer ID.'
          type: integer
        reportsToId:
          description: 'Exact match against an integer ID.'
          type: integer
        shirtSizeId:
          description: 'Exact match against an integer ID.'
          type: integer
        stateId:
          description: 'Exact match against an integer ID.'
          type: integer
        taxTypeId:
          description: 'Exact match against an integer ID.'
          type: integer
        terminationReasonId:
          description: 'Exact match against an integer ID.'
          type: integer
        terminationRegrettableId:
          description: 'Exact match against an integer ID.'
          type: integer
        terminationTypeId:
          description: 'Exact match against an integer ID.'
          type: integer
        tShirtSizeId:
          description: 'Exact match against an integer ID.'
          type: integer
        vaccinationStatusId:
          description: 'Exact match against an integer ID.'
          type: integer
        vaccineReceivedId:
          description: 'Exact match against an integer ID.'
          type: integer
        isManager:
          description: 'Exact match (true/false).'
          type: boolean
        proofOfVaccination:
          description: 'Exact match (true/false).'
          type: boolean
        age:
          description: 'Exact numeric match.'
          type: number
        tenure:
          description: 'Exact numeric match.'
          type: number
        hoursPerPayCycle:
          description: 'Exact numeric match.'
          type: number
        birthDate:
          description: 'Exact match against an ISO 8601 date (YYYY-MM-DD).'
          type: string
          format: date
        compensationEffectiveDate:
          description: 'Exact match against an ISO 8601 date (YYYY-MM-DD).'
          type: string
          format: date
        compensationEndDate:
          description: 'Exact match against an ISO 8601 date (YYYY-MM-DD).'
          type: string
          format: date
        contractEndDate:
          description: 'Exact match against an ISO 8601 date (YYYY-MM-DD).'
          type: string
          format: date
        employmentStatusEffectiveDate:
          description: 'Exact match against an ISO 8601 date (YYYY-MM-DD).'
          type: string
          format: date
        finalDoseAdministrationDate:
          description: 'Exact match against an ISO 8601 date (YYYY-MM-DD).'
          type: string
          format: date
        finalPayDate:
          description: 'Exact match against an ISO 8601 date (YYYY-MM-DD).'
          type: string
          format: date
        hireDate:
          description: 'Exact match against an ISO 8601 date (YYYY-MM-DD).'
          type: string
          format: date
        jobInformationEffectiveDate:
          description: 'Exact match against an ISO 8601 date (YYYY-MM-DD).'
          type: string
          format: date
        originalHireDate:
          description: 'Exact match against an ISO 8601 date (YYYY-MM-DD).'
          type: string
          format: date
        probationEndDate:
          description: 'Exact match against an ISO 8601 date (YYYY-MM-DD).'
          type: string
          format: date
        terminationDate:
          description: 'Exact match against an ISO 8601 date (YYYY-MM-DD).'
          type: string
          format: date
        firstName:
          description: 'This will match any employees whose first name contains this string (case insensitive)'
          type: string
        lastName:
          description: 'This will match any employees whose last name contains this string (case insensitive)'
          type: string
        jobTitleName:
          description: 'This will match any employees whose current job title descriptor contains this string (case insensitive)'
          type: string
        status:
          description: 'Employee status'
          type: string
          enum:
            - active
            - inactive
        ids:
          description: 'List of employee IDs for batch fetch. Documented form: repeated keys (`filter[ids][]=123&filter[ids][]=124`). For backward compatibility, the endpoint also accepts a single comma-separated string (`filter[ids]=123,124`).'
          type: array
          items:
            type: integer
          example:
            - 123
            - 124
      type: object
    GetEmployeesResponseObject:
      description: 'Complete response object for employee list API containing employee data, metadata, and navigation links'
      required:
        - data
        - meta
        - _links
      properties:
        data:
          $ref: '#/components/schemas/GetEmployeesEmployeeResponseCollection'
          description: 'Collection of employee data objects'
        meta:
          $ref: '#/components/schemas/CursorPagedResponseMetadata'
          description: 'Metadata information including total count and pagination details'
        _links:
          description: 'Navigation links for API pagination'
          properties:
            self:
              description: 'Link to current page'
              properties:
                href:
                  description: 'URL of the current page'
                  type: string
              type: object
            next:
              description: 'Link to next page'
              properties:
                href:
                  description: 'URL of the next page'
                  type: string
              type: object
            prev:
              description: 'Link to previous page'
              properties:
                href:
                  description: 'URL of the previous page'
                  type: string
              type: object
          type: object
      type: object
    EmployeeOptionalField:
      description: 'Valid field names that can be passed via the `fields` query parameter to request additional employee data beyond the default set.'
      type: string
      enum:
        - addressLine1
        - addressLine2
        - age
        - allergies
        - bestEmail
        - birthDate
        - birthplace
        - citizenship
        - citizenshipId
        - city
        - compensationChangeReason
        - compensationChangeReasonId
        - compensationComment
        - compensationEffectiveDate
        - compensationEndDate
        - contractEndDate
        - country
        - countryId
        - departmentId
        - departmentName
        - dietaryRestrictions
        - displayName
        - divisionId
        - divisionName
        - eeoJobCategory
        - eeoJobCategoryId
        - ein
        - eligibleForRehire
        - eligibleForRehireId
        - employeeName
        - employeeNumber
        - employmentStatusComment
        - employmentStatusEffectiveDate
        - employmentStatusId
        - employmentStatusName
        - employmentType
        - employmentTypeId
        - ethnicity
        - ethnicityId
        - facebookUrl
        - finalDoseAdministrationDate
        - finalPayDate
        - firstNameLastName
        - firstNameMiddleInitial
        - flsaCode
        - flsaCodeId
        - gender
        - genderIdentity
        - genderIdentityId
        - hireDate
        - homeEmail
        - homePhone
        - hoursPerPayCycle
        - instagramUrl
        - isManager
        - jacketSize
        - jacketSizeId
        - jobInformationEffectiveDate
        - jobTitleId
        - linkedinUrl
        - locationId
        - locationName
        - maritalStatus
        - middleInitial
        - middleName
        - mobilePhone
        - nationalId
        - nationalInsuranceCategory
        - nationalInsuranceCategoryId
        - nationality
        - nationalityId
        - nickName
        - nin
        - noticePeriod
        - noticePeriodId
        - originalHireDate
        - overtime
        - overtimeRate
        - paidPer
        - payRate
        - paySchedule
        - payScheduleId
        - payType
        - pinterestUrl
        - preferredNameLastName
        - probationEndDate
        - pronouns
        - pronounsId
        - proofOfVaccination
        - reportsToId
        - reportsToName
        - secondaryLanguage
        - shirtSize
        - shirtSizeId
        - sin
        - skypeUsername
        - ssn
        - state
        - stateId
        - taxTypeId
        - teams
        - tenure
        - terminationDate
        - terminationReason
        - terminationReasonId
        - terminationRegrettable
        - terminationRegrettableId
        - terminationType
        - terminationTypeId
        - tShirtSize
        - tShirtSizeId
        - twitterUrl
        - userId
        - vaccinationStatus
        - vaccinationStatusId
        - vaccineReceived
        - vaccineReceivedId
        - veteranStatus
        - veteranStatusId
        - workEmail
        - workPhone
        - workPhoneExtension
        - zipcode
    LevelsAndBands-ColumnMap:
      description: 'Schema for levels and bands column map'
      properties:
        expectedColumnKey:
          description: 'Expected column key'
          oneOf:
            -
              type: string
            -
              type: 'null'
        columnName:
          description: 'Column name'
          type: string
      type: object
    LevelsAndBands-GroupStatusCounts:
      description: 'Schema for levels and bands group status counts'
      properties:
        draft:
          description: 'Number of levels/bands in draft status'
          type: integer
        historic:
          description: 'Number of levels/bands in historic status'
          type: integer
        published:
          description: 'Number of levels/bands in published status'
          type: integer
      type: object
    LevelsAndBands-Employee:
      description: 'Job titles employee object'
      properties:
        id:
          description: 'Employee id'
          type: integer
        name:
          description: 'Employee name'
          type: string
      type: object
    LevelsAndBands-JobTitleAssignment:
      description: 'Job title assignment to a level'
      properties:
        jobTitleId:
          description: 'Job title id'
          type: integer
        levelId:
          description: 'Compensation level id'
          type: integer
      type: object
    LevelsAndBands-JobTitleAssignmentsRequest:
      description: 'Request to bulk assign job titles to levels'
      required:
        - jobTitles
      properties:
        jobTitles:
          description: 'Array of job title assignments'
          type: array
          items:
            $ref: '#/components/schemas/LevelsAndBands-JobTitleAssignment'
      type: object
    LevelsAndBands-JobTitle:
      description: 'Job Titles Job Title Data Object'
      properties:
        id:
          description: 'Job title id'
          type: integer
        jobTitle:
          description: 'Job title name'
          type: string
      type: object
    LevelsAndBands-JobTitleWithEmployees:
      description: 'Levels and bands job titles with employees object'
      properties:
        id:
          description: 'Job id'
          type: integer
        title:
          description: 'Job title'
          type: string
        employees:
          description: 'Employees with this job title'
          type: array
          items:
            $ref: '#/components/schemas/LevelsAndBands-Employee'
        levelId:
          description: 'Associated level ID, null if not associated with any level'
          oneOf:
            -
              type: integer
            -
              type: 'null'
      type: object
    LevelsAndBands-JobTitlesCollection:
      description: 'Job Titles Collection'
      type: object
    LevelsAndBands-Group:
      title: 'Group Data Object'
      description: 'Represents a compensation level group with its levels'
      properties:
        id:
          description: 'The group ID. Null for new groups.'
          oneOf:
            -
              type: integer
            -
              type: 'null'
        groupName:
          description: 'The name of the group. A warning will be shown if blank.'
          oneOf:
            -
              type: string
            -
              type: 'null'
        levels:
          description: 'Array of levels in this group'
          type: array
          items:
            $ref: '#/components/schemas/LevelsAndBands-Level'
      type: object
    LevelsAndBands-Level:
      title: 'Level Data Object'
      description: 'Represents a single compensation level'
      properties:
        levelId:
          description: 'The level ID. Null for new levels.'
          oneOf:
            -
              type: integer
            -
              type: 'null'
        levelName:
          description: 'The name of the level'
          oneOf:
            -
              type: string
            -
              type: 'null'
        payBand:
          $ref: '#/components/schemas/LevelsAndBands-PayBand'
          description: 'Pay band associated with this level'
        jobTitles:
          description: 'Array of job titles associated with this level'
          type: array
          items:
            $ref: '#/components/schemas/LevelsAndBands-JobTitle'
        errors:
          description: 'Array of validation errors for this level'
          type: array
          items:
            type: string
        warnings:
          description: 'Array of validation warnings for this level'
          type: array
          items:
            type: string
      type: object
    LevelsAndBands-UploadResponse:
      description: 'Schema for levels and bands import upload response'
      properties:
        uploadData:
          description: 'Upload data'
          type: array
          items:
            type: string
        columnMap:
          description: 'Column map'
          type: array
          items:
            $ref: '#/components/schemas/LevelsAndBands-ColumnMap'
      type: object
    LevelsAndBands-ErrorWarningIdentifier:
      title: 'Error Warning Identifier'
      description: 'Object for compensation level groups and levels'
      required:
        - groupId
        - levelId
      properties:
        groupId:
          description: 'Group ID'
          type: integer
        levelId:
          description: 'Level ID'
          type: integer
      type: object
    LevelsAndBands-JobTitlesStatus:
      title: 'Job Titles Status'
      description: 'Object for compensation level groups and levels'
      required:
        - isComplete
        - errors
        - warnings
      properties:
        isComplete:
          description: 'Is the job titles step complete'
          type: boolean
        errors:
          description: 'Array of errors'
          type: array
          items:
            type: string
        warnings:
          description: 'Array of warnings'
          type: array
          items:
            $ref: '#/components/schemas/LevelsAndBands-JobTitlesCollection'
      type: object
    LevelsAndBands-LevelsAndBands:
      title: 'Levels and Bands'
      description: 'Object for compensation level groups and levels'
      required:
        - groups
      properties:
        groups:
          description: 'Array of compensation level groups to upsert'
          type: array
          items:
            $ref: '#/components/schemas/LevelsAndBands-Group'
      type: object
    LevelsAndBands-LevelsAndBandsStatus:
      title: 'Levels and Bands Status'
      description: 'Object for compensation level groups and levels'
      required:
        - levels
        - payBands
        - jobTitles
        - review
      properties:
        levels:
          $ref: '#/components/schemas/LevelsAndBands-StepStatus'
          description: 'Object for compensation level groups and levels'
        payBands:
          $ref: '#/components/schemas/LevelsAndBands-StepStatus'
          description: 'Object for compensation level groups and levels'
        jobTitles:
          $ref: '#/components/schemas/LevelsAndBands-JobTitlesStatus'
          description: 'Object for compensation level groups and levels'
        review:
          $ref: '#/components/schemas/LevelsAndBands-StepStatus'
          description: 'Object for compensation level groups and levels'
      type: object
    LevelsAndBands-StepStatus:
      title: 'Step Status'
      description: 'Object for compensation level groups and levels'
      required:
        - isComplete
        - errors
        - warnings
      properties:
        isComplete:
          description: 'Whether the step is complete'
          type: boolean
        errors:
          description: 'Array of errors'
          type: array
          items:
            $ref: '#/components/schemas/LevelsAndBands-ErrorWarningIdentifier'
        warnings:
          description: 'Array of warnings'
          type: array
          items:
            $ref: '#/components/schemas/LevelsAndBands-ErrorWarningIdentifier'
      type: object
    LevelsAndBands-PayBand:
      description: 'Pay band data object'
      properties:
        levelId:
          description: 'The level ID'
          oneOf:
            -
              type: integer
            -
              type: 'null'
        min:
          description: 'The minimum value for the level'
          type: number
          format: float
        mid:
          description: 'The middle value for the level'
          type: number
          format: float
        max:
          description: 'The maximum value for the level'
          type: number
          format: float
        currencyCode:
          description: 'The currency code for the level'
          oneOf:
            -
              type: string
            -
              type: 'null'
        percentageRange:
          description: 'The percentage range for the level'
          type: number
          format: float
        compensationType:
          description: 'The compensation type for the level'
          type: string
      type: object
    CompletedQuestionsAndResponseDataObject:
      description: 'Data object for completed questions and responses'
      properties:
        question:
          description: 'The question that was completed'
          type: string
        isRequired:
          description: 'Indicates if the question is required'
          type: boolean
        archived:
          description: 'Indicates if the question is archived'
          type: boolean
        sortOrder:
          description: 'The sort order of the question'
          type: integer
        employeeId:
          description: 'The employee ID associated with the question'
          type: integer
        employeePersonalQuestionId:
          description: 'The employee personal question ID associated with the question'
          type: integer
        employeeResponse:
          description: 'The response to the question'
          type: string
        hidden:
          description: 'Indicates if the question is hidden'
          type: boolean
      type: object
    LocationResponseObject:
      description: 'Transformer for location data with nested address structure'
      properties:
        id:
          description: 'Unique identifier for the location'
          type: string
        label:
          description: 'Display name for the location'
          type: string
        archived:
          description: 'Whether the location is archived'
          type: boolean
        manageable:
          description: 'Whether the location is manageable'
          type: boolean
        address:
          description: 'Address information for the location'
          properties:
            address1:
              description: 'Address line 1. Should only be null if this is a remote location'
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            address2:
              description: 'Address line 2'
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            city:
              description: 'City name. Should only be null if this is a remote location'
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            stateId:
              description: 'State identifier. Should only be null if this is a remote location'
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            state:
              description: 'State information. Optional and requested via expand property from the request'
              properties:
                id:
                  description: 'State identifier'
                  type: string
                name:
                  description: 'State name'
                  type: string
                abbreviation:
                  description: 'State abbreviation'
                  type: string
              type: object
            zipcode:
              description: 'Postal code. Should only be null if this is a remote location'
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            countryId:
              description: 'Country identifier. Should only be null if this is a remote location'
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            country:
              description: 'Country information. Optional and requested via expand property from the request'
              properties:
                id:
                  description: 'Country identifier'
                  type: string
                name:
                  description: 'Country name'
                  type: string
                isoCode:
                  description: 'Country ISO code'
                  type: string
              type: object
            timezone:
              description: 'Timezone name'
              oneOf:
                -
                  type: string
                -
                  type: 'null'
            remoteLocation:
              description: 'Whether this is a remote location'
              type: boolean
          type: object
        createdAt:
          description: 'ISO 8601 timestamp of when the location was created'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        archivedAt:
          description: 'ISO 8601 timestamp of when the location was archived'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
      type: object
    Project-TimeTrackingProjectCollection-V1:
      description: 'Collection of time tracking projects.'
      type: array
      items:
        $ref: '#/components/schemas/Project-TimeTrackingProject-V1'
    Project-TimeTrackingProject-V1:
      description: 'A time tracking project. Projects are organizational units used to classify hours worked.'
      properties:
        id:
          description: 'The ID of the project.'
          type: integer
          readOnly: true
          example: 1
        name:
          description: 'The name of the project.'
          type: string
          example: 'Project A'
        billable:
          description: 'Whether or not the project is billable.'
          type: boolean
          example: false
        includeInPayroll:
          description: 'Whether hours logged to this project will show in payroll and payroll reports.'
          type: boolean
          example: false
        allEmployeesAssigned:
          description: 'Whether all time & attendance employees are assigned or not.'
          type: boolean
          example: true
        archived:
          description: 'Whether or not the project is archived.'
          type: boolean
          example: false
        hasTasks:
          description: 'Whether time is logged to tasks under the project (true) or directly to the project (false).'
          type: boolean
          example: true
        createdAt:
          description: 'ISO 8601 timestamp when the project was created.'
          readOnly: true
          example: '2025-10-15T16:00:00Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        updatedAt:
          description: 'ISO 8601 timestamp when the project was last updated.'
          readOnly: true
          example: '2025-10-15T16:00:00Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        deletedAt:
          description: 'ISO 8601 timestamp when the project was deleted.'
          readOnly: true
          example: '2025-10-15T16:00:00Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        employeeIds:
          description: 'Array of employeeIds assigned to the project.'
          type: array
          items:
            type: integer
          example:
            - 1
            - 2
            - 3
      type: object
    Project-UpdateTimeTrackingProject-V1:
      description: 'Data contract for partially updating a time tracking project. All fields are optional; only fields present in the request body will be updated.'
      properties:
        name:
          description: 'The name of the project.'
          type: string
          example: 'Project A'
        billable:
          description: 'Whether or not the project is billable.'
          type: boolean
          example: true
        includeInPayroll:
          description: 'Whether hours logged to this project will show in payroll and payroll reports.'
          type: boolean
          example: true
        allEmployeesAssigned:
          description: 'Whether all time & attendance employees are assigned or not.'
          type: boolean
          example: true
        archived:
          description: 'Whether or not the project is archived.'
          type: boolean
          example: false
        employeeIds:
          description: 'Array of employee IDs to assign to the project. Replaces the current assignment list when present; an empty array clears all assignments.'
          type: array
          items:
            type: integer
          example:
            - 1
            - 2
            - 3
        hasTasks:
          description: 'Toggles whether time is logged directly to the project (false) or to specific tasks on the project (true). Setting to true requires the project to already have at least one active task; setting to false leaves existing tasks unchanged.'
          type: boolean
          example: true
      type: object
    Project-TimeTrackingTaskCollection-V1:
      description: 'Collection of time tracking tasks for a project.'
      type: array
      items:
        $ref: '#/components/schemas/Project-TimeTrackingTask-V1'
    Project-CreateTimeTrackingProjectTask-V1:
      description: 'Payload to create a task on a time tracking project.'
      required:
        - name
      properties:
        name:
          description: 'Display name of the task.'
          type: string
          maxLength: 50
          example: 'Client onboarding'
        billable:
          description: 'Whether hours on this task are billable. Defaults to false when omitted.'
          example: true
          oneOf:
            -
              type: boolean
            -
              type: 'null'
      type: object
    Project-TimeTrackingTask-V1:
      description: 'A task within a time tracking project. Tasks are sub-organizational units used to further classify hours worked.'
      properties:
        id:
          description: 'The ID of the task.'
          type: integer
          readOnly: true
          example: 1
        name:
          description: 'The name of the task.'
          type: string
          example: 'Task A'
        projectId:
          description: 'The ID of the project this task belongs to.'
          type: integer
          example: 1
        billable:
          description: 'Whether the task is billable.'
          type: boolean
          example: false
        createdAt:
          description: 'ISO 8601 timestamp when the task was created.'
          readOnly: true
          example: '2025-10-15T16:00:00Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        updatedAt:
          description: 'ISO 8601 timestamp when the task was last updated.'
          readOnly: true
          example: '2025-10-15T16:00:00Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        deletedAt:
          description: 'ISO 8601 timestamp when the task was deleted.'
          readOnly: true
          example: '2025-10-15T16:00:00Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
      type: object
    Project-UpdateTimeTrackingProjectTask-V1:
      description: 'Payload to partially update a task on a time tracking project. All fields are optional; at least one field must be provided.'
      minProperties: 1
      properties:
        name:
          description: 'Display name of the task.'
          type: string
          maxLength: 50
          example: 'Client onboarding'
        billable:
          description: 'Whether hours on this task are billable.'
          type: boolean
          example: true
      type: object
    Scheduling-CreateScheduleRequest-V1:
      description: 'Request object for creating or updating a schedule'
      required:
        - name
        - locationId
        - startOfWeek
      properties:
        name:
          description: 'The name of the schedule'
          type: string
          example: 'Morning Shift'
        locationId:
          description: 'The ID of the location the schedule belongs to'
          type: integer
          example: 1
        timezone:
          description: 'The timezone of the schedule. Required for remote locations. For non-remote locations, the location timezone is used.'
          example: America/Denver
          oneOf:
            -
              type: string
            -
              type: 'null'
        startOfWeek:
          description: 'The starting day of the week for the schedule'
          type: string
          example: Monday
        earlyClockInThreshold:
          description: 'The threshold (in minutes) that an employee is allowed to clock in early to a shift on this schedule'
          type: integer
          example: 15
        managerUserIds:
          description: 'User IDs of managers for this schedule'
          type: array
          items:
            type: integer
          example:
            - 1
            - 2
            - 3
        employeeIds:
          description: 'Employee IDs assigned to this schedule'
          type: array
          items:
            type: integer
          example:
            - 10
            - 20
            - 30
      type: object
    Scheduling-Schedule-V1:
      description: 'A shift schedule for a specific location, which organizes shifts for employees'
      properties:
        id:
          description: 'The UUID of the schedule'
          type: string
          format: uuid
          readOnly: true
          example: 0199de9b-6bcb-77e7-941d-3caf74b9a372
        name:
          description: 'The name of the schedule'
          type: string
          example: 'Main Schedule'
        locationId:
          description: 'The ID of the location the schedule belongs to'
          type: integer
          example: 123
        timezone:
          description: 'The timezone of the schedule, provide if location is missing timezone'
          type: string
          example: America/Denver
        startOfWeek:
          description: 'The starting day of the week for the schedule'
          type: string
          example: Monday
        earlyClockInThreshold:
          description: 'The threshold (in minutes) that an employee is allowed to clock in early to a shift on this schedule'
          type: integer
          example: 10
        managerUserIds:
          description: 'What users manage this schedule'
          type: array
          items:
            type: integer
          example:
            - 1
            - 2
        employeeIds:
          description: 'What employees are assigned to this schedule'
          type: array
          items:
            type: integer
          example:
            - 10
            - 20
        createdAt:
          description: 'UTC timestamp when the schedule was created'
          type: string
          format: date-time
          readOnly: true
          example: '2025-01-01T06:00:00Z'
        updatedAt:
          description: 'UTC timestamp when the schedule was last updated'
          readOnly: true
          example: '2025-01-01T06:00:00Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        deletedAt:
          description: 'UTC timestamp when the schedule was deleted'
          readOnly: true
          example: '2025-01-01T06:00:00Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
      type: object
    Scheduling-UpdateScheduleRequest-V1:
      description: 'Request object for updating a schedule'
      properties:
        name:
          description: 'The name of the schedule'
          type: string
          example: 'Morning Shift'
        locationId:
          description: 'The ID of the location the schedule belongs to'
          type: integer
          example: 1
        timezone:
          description: 'The timezone of the schedule. Required if location does not have a timezone. Error if location already has a timezone.'
          type: string
          example: America/Denver
        startOfWeek:
          description: 'The starting day of the week for the schedule'
          type: string
          example: Monday
        earlyClockInThreshold:
          description: 'The threshold (in minutes) that an employee is allowed to clock in early to a shift on this schedule'
          type: integer
          example: 15
        managerUserIds:
          description: 'User IDs of managers for this schedule'
          type: array
          items:
            type: integer
          example:
            - 1
            - 2
            - 3
        employeeIds:
          description: 'Employee IDs assigned to this schedule'
          type: array
          items:
            type: integer
          example:
            - 10
            - 20
            - 30
      type: object
    Scheduling-PublishShiftsFailure-V1:
      description: 'A shift that failed to publish due to a conflict'
      required:
        - shiftId
        - reason
      properties:
        shiftId:
          description: 'The ID of the shift that failed to publish.'
          type: string
          example: 019aa09f-0712-74cb-9186-8ad0eb6038dc
        reason:
          description: 'The reason the shift failed to publish.'
          type: string
          example: 'Employee is already assigned to a published shift that overlaps with this shift.'
      type: object
    Scheduling-PublishShiftsResult-V1:
      description: 'Result of a bulk publish operation, containing both successfully published shifts and any failures'
      required:
        - published
        - failed
      properties:
        published:
          description: 'The shifts that were successfully published.'
          type: array
          items:
            $ref: '#/components/schemas/Scheduling-SchedulingShift-V1'
        failed:
          description: 'The shifts that failed to publish due to conflicts.'
          type: array
          items:
            $ref: '#/components/schemas/Scheduling-PublishShiftsFailure-V1'
      type: object
    Scheduling-SchedulingShift-V1:
      description: 'A shift for scheduling'
      required:
        - id
        - scheduleId
        - status
        - color
        - start
        - end
        - timezone
      properties:
        id:
          description: 'The ID of the shift. This can be the UUIDv7 of the shift instance, or a composite ID (<recurringShiftDefinitionId>_<recurrenceId>) for uninstantiated recurring shifts.'
          type: string
          readOnly: true
          example: 019aa09f-0712-74cb-9186-8ad0eb6038dc_20240101T093000
        scheduleId:
          description: 'The ID of the schedule the shift belongs to.'
          type: string
          format: uuid
          example: 0199de9b-6bcb-77e7-941d-3caf74b9a372
        name:
          description: 'The name of the shift.'
          example: 'Morning Shift'
          oneOf:
            -
              type: string
            -
              type: 'null'
        status:
          description: 'The status of the shift.'
          enum:
            - planned
            - published
            - cancelled
            - deleted
        color:
          description: '6 character color hex code.'
          type: string
          example: 00FFAA
        capacity:
          description: 'The expected headcount for the shift. Defaults to 1 if unset.'
          example: 1
          oneOf:
            -
              type: integer
            -
              type: 'null'
        start:
          description: 'UTC timestamp of the start of the shift.'
          example: '2025-01-01T06:00:00Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        end:
          description: 'UTC timestamp of the end of the shift.'
          example: '2025-01-01T14:00:00Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        timezone:
          description: 'The timezone for the shift.'
          type: string
          example: America/Denver
        recurrenceRule:
          description: 'The recurrence rule. Follows the iCalendar RFC 5545 RRULE syntax.'
          example: FREQ=WEEKLY;INTERVAL=1
          oneOf:
            -
              type: string
            -
              type: 'null'
        recurrenceId:
          description: 'The stable identifier for a recurrence, which is the original start of shift according to recurrence rules. Null for non-recurring shifts.'
          example: 20240101T090000
          oneOf:
            -
              type: string
            -
              type: 'null'
        recurrenceDtstart:
          description: 'The UTC start time for a recurring shift and the anchor point for the recurrence rule. Null for non-recurring shifts.'
          example: '2024-01-01T09:00:00Z'
          oneOf:
            -
              type: string
            -
              type: 'null'
        recurrenceDtend:
          description: 'The UTC end time for a recurring shift. Null for non-recurring shifts.'
          example: '2024-12-31T23:59:59Z'
          oneOf:
            -
              type: string
            -
              type: 'null'
        recurrenceUntil:
          description: 'The UTC datetime when the recurrence series stops generating new occurrences (UNTIL in RRULE). Null for non-recurring shifts.'
          example: '2024-12-31T23:59:59Z'
          oneOf:
            -
              type: string
            -
              type: 'null'
        employeeIds:
          description: 'The list of employee IDs currently assigned.'
          type: array
          items:
            type: integer
          example:
            - 10
            - 20
        unpublishedChanges:
          description: 'The pending changes that have not been published.'
          oneOf:
            -
              type: object
            -
              type: 'null'
        createdAt:
          description: 'UTC timestamp when the shift was created'
          type: string
          format: date-time
          readOnly: true
          example: '2025-01-01T06:00:00Z'
        updatedAt:
          description: 'UTC timestamp when the shift was last updated'
          readOnly: true
          example: '2025-01-01T06:00:00Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        deletedAt:
          description: 'UTC timestamp when the shift was deleted'
          readOnly: true
          example: '2025-01-01T06:00:00Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
      type: object
    Scheduling-Timezone-V1:
      description: 'A timezone resource with its name and current UTC offset.'
      required:
        - name
        - offset
      properties:
        name:
          description: 'The timezone identifier.'
          type: string
          example: America/Denver
        offset:
          description: 'The current UTC offset for this timezone.'
          type: string
          example: '-7:00'
      type: object
    ShiftDifferential-TimeTrackingShiftDifferentialCollection-V1:
      description: 'Collection of time tracking shift differentials.'
      type: array
      items:
        $ref: '#/components/schemas/ShiftDifferential-TimeTrackingShiftDifferential-V1'
    ShiftDifferential-TimeTrackingShiftDifferential-V1:
      description: 'A shift differential rule with time windows and employee assignments.'
      properties:
        id:
          description: 'The ID of the shift differential.'
          type: integer
          readOnly: true
          example: 1
        name:
          description: 'The name of the shift differential rule.'
          type: string
          maxLength: 50
          example: 'Night Shift Premium'
        rate:
          description: 'The differential pay rate as a decimal string.'
          type: string
          example: '12.00'
        rateType:
          description: 'How the rate is applied.'
          type: string
          enum:
            - AMOUNT
            - PERCENT
          example: AMOUNT
        allowAllEmployees:
          description: 'Whether all time & attendance employees are assigned.'
          type: boolean
          example: true
        times:
          description: 'Time windows when this differential applies.'
          type: array
          items:
            $ref: '#/components/schemas/ShiftDifferential-TimeTrackingShiftDifferentialTime-V1'
        employeeIds:
          description: 'Employee IDs assigned to this shift differential.'
          type: array
          items:
            type: integer
          example:
            - 1
            - 2
            - 3
        createdAt:
          description: 'ISO 8601 timestamp when the shift differential was created.'
          readOnly: true
          example: '2026-01-29T18:52:50Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        updatedAt:
          description: 'ISO 8601 timestamp when the shift differential was last updated.'
          readOnly: true
          example: '2026-02-09T17:56:06Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        archivedAt:
          description: 'ISO 8601 timestamp when the shift differential was archived.'
          readOnly: true
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        deletedAt:
          description: 'ISO 8601 timestamp when the shift differential was deleted.'
          readOnly: true
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
      type: object
    ShiftDifferential-TimeTrackingShiftDifferentialTime-V1:
      description: 'A time window when a shift differential pay rate applies.'
      properties:
        id:
          description: 'The ID of the time window.'
          type: integer
          readOnly: true
          example: 22
        startDay:
          description: 'Start day of the time window.'
          type: string
          enum:
            - MONDAY
            - TUESDAY
            - WEDNESDAY
            - THURSDAY
            - FRIDAY
            - SATURDAY
            - SUNDAY
          example: MONDAY
        endDay:
          description: 'End day of the time window.'
          type: string
          enum:
            - MONDAY
            - TUESDAY
            - WEDNESDAY
            - THURSDAY
            - FRIDAY
            - SATURDAY
            - SUNDAY
          example: MONDAY
        start:
          description: 'Start time in HH:MM format (24-hour).'
          type: string
          example: '00:00'
        end:
          description: 'End time in HH:MM format (24-hour).'
          type: string
          example: '04:00'
      type: object
    EmployeeTimesheetEntryCollectionTransformer:
      description: 'Class EmployeeTimesheetEntryCollectionTransformer'
      type: array
      items:
        $ref: '#/components/schemas/EmployeeTimesheetEntryTransformer'
    EmployeeTimesheetEntryTransformer:
      properties:
        id:
          description: 'Timesheet entry ID'
          type: integer
        employeeId:
          description: 'Employee ID'
          type: integer
        type:
          description: 'Entry type'
          type: string
        date:
          description: 'Date of the timesheet'
          type: string
          format: date
        start:
          description: 'Start time'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        end:
          description: 'End time'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        timezone:
          description: Timezone
          oneOf:
            -
              type: string
            -
              type: 'null'
        hours:
          description: 'Hours worked'
          oneOf:
            -
              type: number
              format: float
            -
              type: 'null'
        note:
          description: Note
          oneOf:
            -
              type: string
            -
              type: 'null'
        projectInfo:
          oneOf:
            -
              $ref: '#/components/schemas/ProjectInfoApiTransformer'
            -
              type: 'null'
        approvedAt:
          description: 'Approved time'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        approved:
          description: 'Whether the timesheet entry is approved'
          type: boolean
        createdAt:
          description: 'Created time'
          type: string
          format: date-time
        updatedAt:
          description: 'Updated time'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
      type: object
    TimesheetEntryInfoApiTransformer:
      properties:
        id:
          description: 'Timesheet entry ID'
          type: integer
        employeeId:
          description: 'Employee ID'
          type: integer
        type:
          description: 'Entry type'
          type: string
        date:
          description: 'Date of the timesheet'
          type: string
          format: date
        start:
          description: 'Start time'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        end:
          description: 'End time'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        timezone:
          description: Timezone
          oneOf:
            -
              type: string
            -
              type: 'null'
        hours:
          description: 'Hours worked'
          oneOf:
            -
              type: number
              format: float
            -
              type: 'null'
        note:
          description: Note
          oneOf:
            -
              type: string
            -
              type: 'null'
        projectInfo:
          oneOf:
            -
              $ref: '#/components/schemas/ProjectInfoApiTransformer'
            -
              type: 'null'
        breakInfo:
          description: 'Break information'
          properties:
            id:
              description: 'Break ID'
              type: integer
            name:
              description: 'Break name'
              type: string
          oneOf:
            -
              type: object
            -
              type: 'null'
      type: object
    TimesheetEntryInfoCollectionApiTransformer:
      description: 'Class TimesheetEntryInfoApiTransformer'
      type: array
      items:
        $ref: '#/components/schemas/TimesheetEntryInfoApiTransformer'
    TimeTracking-CreateOrUpdateTimeTrackingBreakWithoutPolicyCollection-V1:
      description: 'Collection of time tracking breaks to create or update without a policy'
      type: array
      items:
        $ref: '#/components/schemas/TimeTracking-CreateOrUpdateTimeTrackingBreakWithoutPolicy-V1'
    TimeTracking-TimeTrackingBreakAssessmentCollection-V1:
      description: 'Collection of time tracking break assessments'
      type: array
      items:
        $ref: '#/components/schemas/TimeTracking-TimeTrackingBreakAssessment-V1'
    TimeTracking-TimeTrackingBreakAvailabilityCollection-V1:
      description: 'Collection of time tracking break availabilities'
      type: array
      items:
        $ref: '#/components/schemas/TimeTracking-TimeTrackingBreakAvailability-V1'
    TimeTracking-TimeTrackingBreakCollection-V1:
      description: 'Collection of time tracking breaks'
      type: array
      items:
        $ref: '#/components/schemas/TimeTracking-TimeTrackingBreak-V1'
    TimeTracking-TimeTrackingBreakPolicyCollection-V1:
      description: 'Collection of time tracking break policies'
      type: array
      items:
        $ref: '#/components/schemas/TimeTracking-TimeTrackingBreakPolicy-V1'
    TimeTracking-CreateOrUpdateTimeTrackingBreakWithoutPolicy-V1:
      description: 'Data contract for creating or updating a break'
      properties:
        id:
          description: 'The unique identifier of the break. If not included, a new break will be created.'
          oneOf:
            -
              type: string
              format: uuid
            -
              type: 'null'
        name:
          description: 'The name of the break'
          type: string
          example: 'Lunch Break'
        paid:
          description: 'Whether the break is paid'
          type: boolean
        duration:
          description: 'Duration of the break in minutes'
          type: integer
          example: 60
        availabilityType:
          description: 'When the break is available to be taken'
          enum:
            - anytime
            - hours_worked
            - time_of_day
        availabilityMinHoursWorked:
          description: 'Minimum hours that must be worked before the break can be taken'
          oneOf:
            -
              type: number
              format: float
            -
              type: 'null'
        availabilityMaxHoursWorked:
          description: 'Maximum hours that can be worked before the break must be taken'
          oneOf:
            -
              type: number
              format: float
            -
              type: 'null'
        availabilityStartTime:
          description: 'Earliest time the break can be taken (HH:MM format)'
          oneOf:
            -
              type: string
              format: time
            -
              type: 'null'
        availabilityEndTime:
          description: 'Latest time the break can be taken (HH:MM format)'
          oneOf:
            -
              type: string
              format: time
            -
              type: 'null'
      type: object
    TimeTracking-CreateTimeTrackingBreak-V1:
      description: 'Data contract for creating a break'
      properties:
        name:
          description: 'The name of the break'
          type: string
          example: 'Lunch Break'
        policyId:
          description: 'The unique identifier of the break policy this break belongs to'
          type: string
          format: uuid
        paid:
          description: 'Whether the break is paid'
          type: boolean
        duration:
          description: 'Duration of the break in minutes'
          type: integer
          example: 60
        availabilityType:
          description: 'When the break is available to be taken'
          enum:
            - anytime
            - hours_worked
            - time_of_day
        availabilityMinHoursWorked:
          description: 'Minimum hours that must be worked before the break can be taken'
          oneOf:
            -
              type: number
              format: float
            -
              type: 'null'
        availabilityMaxHoursWorked:
          description: 'Maximum hours that can be worked before the break must be taken'
          oneOf:
            -
              type: number
              format: float
            -
              type: 'null'
        availabilityStartTime:
          description: 'Earliest time the break can be taken (HH:MM format)'
          oneOf:
            -
              type: string
              format: time
            -
              type: 'null'
        availabilityEndTime:
          description: 'Latest time the break can be taken (HH:MM format)'
          oneOf:
            -
              type: string
              format: time
            -
              type: 'null'
      type: object
    TimeTracking-CreateTimeTrackingBreakPolicy-V1:
      description: 'Data contract for creating a break policy'
      required:
        - name
      properties:
        name:
          description: 'Name of the break policy'
          type: string
          example: California
        description:
          description: 'Optional description of the break policy'
          example: 'All breaks for California compliance'
          oneOf:
            -
              type: string
            -
              type: 'null'
        allEmployeesAssigned:
          description: 'Whether all employees are assigned to this break policy'
          type: boolean
          example: true
        breaks:
          $ref: '#/components/schemas/TimeTracking-CreateOrUpdateTimeTrackingBreakWithoutPolicyCollection-V1'
        employeeIds:
          description: 'The employees that should be assigned to this policy'
          type: array
          items:
            type: integer
      type: object
    TimeTracking-SyncTimeTrackingBreakPolicy-V1:
      description: 'Data contract for syncing a break policy'
      properties:
        name:
          description: 'Name of the break policy'
          type: string
          example: California
        description:
          description: 'Optional description of the break policy'
          example: 'All breaks for California compliance'
          oneOf:
            -
              type: string
            -
              type: 'null'
        allEmployeesAssigned:
          description: 'Whether all employees are assigned to this break policy'
          type: boolean
          example: true
        breaks:
          $ref: '#/components/schemas/TimeTracking-CreateOrUpdateTimeTrackingBreakWithoutPolicyCollection-V1'
        employeeIds:
          description: 'The employees that should be assigned to this policy'
          type: array
          items:
            type: integer
      type: object
    TimeTracking-TimeTrackingBreak-V1:
      description: 'A break for time tracking belonging to a break policy'
      properties:
        id:
          description: 'The unique identifier for the time tracking break'
          type: string
          format: uuid
          readOnly: true
          example: 0199de9b-6bcb-77e7-941d-3caf74b9a372
        name:
          description: 'The name of the break'
          type: string
          example: 'Lunch Break'
        policyId:
          description: 'The unique identifier of the break policy this break belongs to'
          type: string
          format: uuid
        paid:
          description: 'Whether the break is paid'
          type: boolean
        duration:
          description: 'Duration of the break in minutes'
          type: integer
          example: 60
        availabilityType:
          description: 'When the break is available to be taken'
          enum:
            - anytime
            - hours_worked
            - time_of_day
        availabilityMinHoursWorked:
          description: 'Minimum hours that must be worked before the break can be taken'
          oneOf:
            -
              type: number
              format: float
            -
              type: 'null'
        availabilityMaxHoursWorked:
          description: 'Maximum hours that can be worked before the break must be taken'
          oneOf:
            -
              type: number
              format: float
            -
              type: 'null'
        availabilityStartTime:
          description: 'Earliest time the break can be taken (HH:MM format)'
          example: '08:00'
          oneOf:
            -
              type: string
              format: time
            -
              type: 'null'
        availabilityEndTime:
          description: 'Latest time the break can be taken (HH:MM format)'
          example: '17:00'
          oneOf:
            -
              type: string
              format: time
            -
              type: 'null'
        createdAt:
          description: 'ISO 8601 timestamp when the break was created'
          type: string
          format: date-time
          readOnly: true
          example: '2025-01-01T06:00:00Z'
        updatedAt:
          description: 'ISO 8601 timestamp when the break was last updated'
          readOnly: true
          example: '2025-01-01T06:00:00Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        deletedAt:
          description: 'ISO 8601 timestamp when the break was deleted'
          readOnly: true
          example: '2025-01-01T06:00:00Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
      type: object
    TimeTracking-TimeTrackingBreakAssessment-V1:
      description: 'A break assessment'
      properties:
        id:
          description: 'The unique identifier for the time tracking break assessment'
          type: string
          format: uuid
          readOnly: true
          example: 0199de9b-6bcb-77e7-941d-3caf74b9a372
        breakId:
          description: 'The unique identifier of the break'
          type: string
          format: uuid
          readOnly: true
          example: 4983000f-b8cd-436a-bbf0-574cfd7a8517
        employeeId:
          description: 'The id of the employee'
          type: integer
          example: 40342
        employeeTimesheetId:
          description: 'The id of the employee timesheet'
          type: integer
          example: 123
        date:
          description: 'The date of the break assessment'
          type: string
          format: date
          example: '2025-01-01'
        timezone:
          description: 'The timezone of the break assessment'
          type: string
          example: America/Los_Angeles
        result:
          description: 'The result of the break assessment'
          enum:
            - compliant
            - violation
            - waived
        availableYmdt:
          description: 'ISO 8601 timestamp when the break became available'
          readOnly: true
          example: '2025-01-01T06:00:00Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        unavailableYmdt:
          description: 'ISO 8601 timestamp when the break became unavailable'
          readOnly: true
          example: '2025-01-01T06:00:00Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        createdAt:
          description: 'ISO 8601 timestamp when the break was created'
          type: string
          format: date-time
          readOnly: true
          example: '2025-01-01T06:00:00Z'
        updatedAt:
          description: 'ISO 8601 timestamp when the break was last updated'
          readOnly: true
          example: '2025-01-01T06:00:00Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        violations:
          description: 'Violations associated with the assessment '
          type: array
          items:
            $ref: '#/components/schemas/TimeTracking-TimeTrackingBreakAssessmentViolation-V1'
        clockEntryIds:
          description: 'Clock entry ids associated with the time tracking break assessment'
          type: array
          items:
            type: integer
        expectedDuration:
          description: 'The expected duration of the break in minutes'
          example: 15
          oneOf:
            -
              type: integer
            -
              type: 'null'
        recordedDuration:
          description: 'The recorded duration of the break in minutes'
          example: 10
          oneOf:
            -
              type: integer
            -
              type: 'null'
        durationDifference:
          description: 'The difference between the expected and recorded duration in minutes'
          example: -5
          oneOf:
            -
              type: integer
            -
              type: 'null'
      type: object
    TimeTracking-TimeTrackingBreakAssessmentViolation-V1:
      description: 'A break assessment violation'
      properties:
        assessmentId:
          description: 'The unique identifier for the time tracking break assessment.'
          type: string
          format: uuid
          readOnly: true
          example: 21e369e9-295c-41fd-8973-08ee3b54f1a6
        type:
          description: 'The type of the violation'
          enum:
            - break_started_early
            - break_started_late
            - break_ended_early
            - break_ended_late
            - break_missed
          readOnly: true
        employeeTimesheetClockEntryId:
          description: 'The id of the employee timesheet clock entry'
          readOnly: true
          example: 101
          oneOf:
            -
              type: number
              format: integer
            -
              type: 'null'
        amount:
          description: 'The amount of the violation'
          readOnly: true
          example: 101
          oneOf:
            -
              type: number
              format: integer
            -
              type: 'null'
      type: object
    TimeTracking-TimeTrackingBreakAvailability-V1:
      description: 'Break availability information for time tracking'
      properties:
        id:
          description: 'The unique identifier for the time tracking break'
          type: string
          format: uuid
          readOnly: true
          example: 0199de9b-6bcb-77e7-941d-3caf74b9a372
        name:
          description: 'The name of the break'
          type: string
          readOnly: true
          example: 'Lunch Break'
        policyId:
          description: 'The unique identifier of the break policy this break belongs to'
          type: string
          format: uuid
          readOnly: true
        paid:
          description: 'Whether the break is paid'
          type: boolean
          readOnly: true
        duration:
          description: 'Duration of the break in minutes'
          type: integer
          readOnly: true
          example: 60
        availabilityType:
          description: 'When the break is available to be taken'
          enum:
            - anytime
            - hours_worked
            - time_of_day
          readOnly: true
        calculatedAt:
          description: 'When the break availability was calculated'
          type: string
          format: date-time
          readOnly: true
        effectiveAt:
          description: 'Timestamp that all time-based calculations are evaluated relative to.'
          type: string
          format: date-time
          readOnly: true
        timezone:
          description: 'The timezone the break availability is calculated in'
          type: string
          readOnly: true
        recordedDuration:
          description: 'The duration of the break that has been recorded so far'
          type: integer
          readOnly: true
        available:
          description: 'Whether the break is currently available to be taken'
          type: boolean
          readOnly: true
        availableAfterMinutesWorked:
          description: 'Minutes of work after which the break becomes available'
          readOnly: true
          oneOf:
            -
              type: integer
            -
              type: 'null'
        availableIn:
          description: 'Minutes until the break becomes available'
          readOnly: true
          oneOf:
            -
              type: integer
            -
              type: 'null'
        unavailableIn:
          description: 'Minutes until the break becomes unavailable'
          readOnly: true
          oneOf:
            -
              type: integer
            -
              type: 'null'
        availableAt:
          description: 'When the break is or becomes available'
          readOnly: true
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        unavailableAt:
          description: 'When the break is or becomes unavailable'
          readOnly: true
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
      type: object
    TimeTracking-TimeTrackingBreakPolicy-V1:
      description: 'A policy governing breaks for time tracking'
      properties:
        id:
          description: 'Unique identifier for the break policy'
          type: string
          format: uuid
          readOnly: true
          example: 0199de9b-6bcb-77e7-941d-3caf74b9a372
        name:
          description: 'Name of the break policy'
          type: string
          example: California
        description:
          description: 'Optional description of the break policy'
          example: 'All breaks for California compliance'
          oneOf:
            -
              type: string
            -
              type: 'null'
        allEmployeesAssigned:
          description: 'Whether all employees are assigned to this break policy'
          type: boolean
          example: true
        createdAt:
          description: 'ISO 8601 timestamp when the break policy was created'
          type: string
          format: date-time
          readOnly: true
          example: '2025-10-15T06:00:00Z'
        updatedAt:
          description: 'ISO 8601 timestamp when the break policy was last updated'
          readOnly: true
          example: '2025-10-15T06:00:00Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        deletedAt:
          description: 'ISO 8601 timestamp when the break policy was deleted'
          readOnly: true
          example: '2025-10-15T06:00:00Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
      type: object
    TimeTracking-TimeTrackingBreakPolicyWithRelations-V1:
      description: 'A break policy for time tracking'
      properties:
        id:
          description: 'Unique identifier for the break policy'
          type: string
          format: uuid
          readOnly: true
          example: 0199de9b-6bcb-77e7-941d-3caf74b9a372
        name:
          description: 'Name of the break policy'
          type: string
          example: California
        description:
          description: 'Optional description of the break policy'
          example: 'All breaks for California compliance'
          oneOf:
            -
              type: string
            -
              type: 'null'
        allEmployeesAssigned:
          description: 'Whether all employees are assigned to this break policy'
          type: boolean
          example: true
        createdAt:
          description: 'ISO 8601 timestamp when the break policy was created'
          type: string
          format: date-time
          readOnly: true
          example: '2025-10-15T06:00:00Z'
        updatedAt:
          description: 'ISO 8601 timestamp when the break policy was last updated'
          readOnly: true
          example: '2025-10-15T06:00:00Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        deletedAt:
          description: 'ISO 8601 timestamp when the break policy was deleted'
          readOnly: true
          example: '2025-10-15T06:00:00Z'
          oneOf:
            -
              type: string
              format: date-time
            -
              type: 'null'
        breaks:
          description: 'The breaks that are assigned to this policy'
          type: array
          items:
            $ref: '#/components/schemas/TimeTracking-TimeTrackingBreak-V1'
        employeeIds:
          description: 'The employees that are assigned to this policy'
          type: array
          items:
            type: integer
      type: object
    TimeTracking-UpdateTimeTrackingBreak-V1:
      description: 'Data contract for partially updating a break. All fields are optional; only fields present in the request body will be updated.'
      properties:
        name:
          description: 'The name of the break'
          type: string
          example: 'Lunch Break'
        policyId:
          description: 'The unique identifier of the break policy this break belongs to'
          type: string
          format: uuid
        paid:
          description: 'Whether the break is paid'
          type: boolean
        duration:
          description: 'Duration of the break in minutes'
          type: integer
        availabilityType:
          description: 'When the break is available to be taken'
          enum:
            - anytime
            - hours_worked
            - time_of_day
        availabilityMinHoursWorked:
          description: 'Minimum hours that must be worked before the break can be taken'
          oneOf:
            -
              type: number
              format: float
            -
              type: 'null'
        availabilityMaxHoursWorked:
          description: 'Maximum hours that can be worked before the break must be taken'
          oneOf:
            -
              type: number
              format: float
            -
              type: 'null'
        availabilityStartTime:
          description: 'Earliest time the break can be taken (HH:MM format)'
          oneOf:
            -
              type: string
              format: time
            -
              type: 'null'
        availabilityEndTime:
          description: 'Latest time the break can be taken (HH:MM format)'
          oneOf:
            -
              type: string
              format: time
            -
              type: 'null'
      type: object
    TimeTracking-UpdateTimeTrackingBreakPolicy-V1:
      description: 'Data contract for updating a break policy'
      properties:
        name:
          description: 'Name of the break policy'
          type: string
          example: California
        description:
          description: 'Optional description of the break policy'
          example: 'All breaks for California compliance'
          oneOf:
            -
              type: string
            -
              type: 'null'
        allEmployeesAssigned:
          description: 'Whether all employees are assigned to this break policy'
          type: boolean
          example: true
      type: object
    ClockEntriesSchema:
      description: 'Request body schema for operations involving multiple clock entries'
      required:
        - entries
      properties:
        entries:
          description: 'Array of clock entries'
          type: array
          items:
            $ref: '#/components/schemas/ClockEntrySchema'
          minItems: 1
      type: object
    ClockEntryIdsSchema:
      description: 'Request body schema for operations involving multiple clock entry IDs'
      required:
        - clockEntryIds
      properties:
        clockEntryIds:
          description: 'Array of clock entry IDs to process'
          type: array
          items:
            type: integer
          minItems: 1
          example:
            - 10
            - 11
            - 12
      type: object
    ClockEntrySchema:
      description: 'Schema for a single clock entry'
      required:
        - employeeId
        - date
        - start
        - end
      properties:
        employeeId:
          description: 'Unique identifier for the employee.'
          type: integer
        date:
          description: 'Date for the timesheet entry. Must be in YYYY-MM-DD format.'
          type: string
          format: date
          example: '2024-01-01'
        start:
          description: 'Start time for the timesheet entry. Local time for the employee. Must be in hh:mm 24 hour format.'
          type: string
          pattern: '^([01]?[0-9]|2[0-3]):[0-5][0-9]$'
          example: '09:00'
        end:
          description: 'End time for the timesheet entry. Local time for the employee. Must be in hh:mm 24 hour format.'
          type: string
          pattern: '^([01]?[0-9]|2[0-3]):[0-5][0-9]$'
          example: '17:00'
        id:
          description: 'The ID of an existing timesheet entry. This can be specified to edit an existing entry.'
          type: integer
        projectId:
          description: 'The ID of the project to associate with the timesheet entry.'
          type: integer
        taskId:
          description: 'The ID of the task to associate with the timesheet entry.'
          type: integer
        note:
          description: 'Optional note to associate with the timesheet entry.'
          type: string
        breakId:
          description: 'Optional break id to associate with the timesheet entry.'
          oneOf:
            -
              type: string
              format: uuid
            -
              type: 'null'
      type: object
    HourEntryIdsSchema:
      description: 'Request body schema for operations involving multiple hour entry IDs'
      required:
        - hourEntryIds
      properties:
        hourEntryIds:
          description: 'Array of hour entry IDs to process'
          type: array
          items:
            type: integer
          minItems: 1
          example:
            - 100
            - 101
            - 102
      type: object
    TotalRewards-BenefitDetailsValues:
      description: 'Schema for a total rewards benefit details value'
      properties:
        name:
          description: Name
          type: string
        companyContributionAmount:
          description: 'Company contribution amount'
          type: string
        companyContributionSubvalue:
          description: 'Source contribution when the amount was currency-converted'
          oneOf:
            -
              type: string
            -
              type: 'null'
        summary:
          description: Summary
          oneOf:
            -
              type: string
            -
              type: 'null'
        url:
          description: Url
          oneOf:
            -
              type: string
            -
              type: 'null'
        benefitModalValuesLink:
          description: 'Link to the benefits modal'
          oneOf:
            -
              type: string
            -
              type: 'null'
      type: object
    TotalRewards-BenefitSectionValues:
      description: 'Schema for a total rewards benefit section'
      properties:
        compSummary:
          $ref: '#/components/schemas/TotalRewards-CompSummaryValues'
          description: 'Compensation summary'
        benefitDetails:
          description: 'Benefit details'
          type: array
          items:
            $ref: '#/components/schemas/TotalRewards-BenefitDetailsValues'
      type: object
    TotalRewards-CalendarSectionValues:
      description: 'Schema for total rewards calendar section'
      properties:
        holidays:
          description: 'List of holidays'
          type: array
          items:
            $ref: '#/components/schemas/TotalRewards-HolidayValue'
        timeOffPolicies:
          description: 'List of time off policies'
          type: array
          items:
            $ref: '#/components/schemas/TotalRewards-TimeOffPolicyValue'
        hourlyAccrualDisclaimer:
          description: 'Hourly accrual disclaimer text'
          oneOf:
            -
              type: string
            -
              type: 'null'
      type: object
    TotalRewards-CompSummaryValues:
      description: 'Schema for total rewards compensation summary values'
      properties:
        title:
          description: Title
          type: string
        subtitle:
          description: Subtitle
          oneOf:
            -
              type: string
            -
              type: 'null'
        value:
          description: Value
          oneOf:
            -
              type: string
            -
              type: 'null'
        subvalue:
          description: 'When conversion applies: localized “(from …)” fragment including the original amount'
          oneOf:
            -
              type: string
            -
              type: 'null'
      type: object
    TotalRewards-CustomDisclaimerInfo:
      description: 'Schema for a total rewards custom disclaimer info'
      properties:
        isViewerAdmin:
          description: 'Is admin currently viewing'
          type: boolean
        customDisclaimer:
          description: 'Custom disclaimer text'
          oneOf:
            -
              type: string
            -
              type: 'null'
      type: object
    TotalRewards-EquityDetailsValues:
      description: 'Schema for total rewards equity details values'
      properties:
        cliffMonths:
          description: 'Cliff months'
          oneOf:
            -
              type: integer
            -
              type: 'null'
        estimatedExerciseCost:
          description: 'Estimated exercise cost'
          oneOf:
            -
              type: string
            -
              type: 'null'
        estimatedExerciseCostSubvalue:
          description: 'Source exercise cost when converted'
          oneOf:
            -
              type: string
            -
              type: 'null'
        estimatedSharePrice:
          description: 'Estimated share price'
          type: string
        estimatedSharePriceSubvalue:
          description: 'Source share price when converted'
          oneOf:
            -
              type: string
            -
              type: 'null'
        estimatedTotalValue:
          description: 'Estimated total value'
          type: string
        estimatedTotalValueSubvalue:
          description: 'Source total when converted'
          oneOf:
            -
              type: string
            -
              type: 'null'
        grantDate:
          description: 'Grant date'
          type: string
          format: date
        grantType:
          description: 'Grant type'
          type: string
        grantName:
          description: 'Grant name'
          oneOf:
            -
              type: string
            -
              type: 'null'
        equityGrowthChartData:
          description: 'Equity growth chart data'
          oneOf:
            -
              type: array
              items:
                $ref: '#/components/schemas/TotalRewards-EquityGrowthChartItem'
            -
              type: 'null'
        numShares:
          description: 'Number of shares'
          type: integer
        numVestedShares:
          description: 'Number of vested shares'
          type: integer
        percentVested:
          description: 'Percent vested'
          type: number
          format: float
        strikePrice:
          description: 'Strike price'
          oneOf:
            -
              type: string
            -
              type: 'null'
        strikePriceSubvalue:
          description: 'Source strike when converted'
          oneOf:
            -
              type: string
            -
              type: 'null'
        vestingMonths:
          description: 'Vesting months'
          oneOf:
            -
              type: integer
            -
              type: 'null'
        vestingSchedule:
          description: 'Vesting schedule'
          type: string
        vestingStartDate:
          description: 'Vesting start date'
          type: string
          format: date
        vestedValue:
          description: 'Vested value'
          type: string
        vestedValueSubvalue:
          description: 'Source vested when converted'
          oneOf:
            -
              type: string
            -
              type: 'null'
      type: object
    TotalRewards-EquityEstimatedValuationValues:
      description: 'Schema for total rewards equity growth valuation'
      properties:
        equityGrowthEstimationItems:
          description: 'Equity estimated growth items'
          type: array
          items:
            $ref: '#/components/schemas/TotalRewards-EquityGrowthEstimation'
        numberOfVestedShares:
          description: 'Number of vested shares'
          type: integer
        numberOfTotalShares:
          description: 'Number of total shares'
          type: integer
        equityGrowthSliderMax:
          description: 'Equity growth slider max value'
          oneOf:
            -
              type: integer
            -
              type: 'null'
        equityGrowthSliderMin:
          description: 'Equity growth slider min value'
          oneOf:
            -
              type: integer
            -
              type: 'null'
      type: object
    TotalRewards-EquityGrowthChartItem:
      description: 'Schema for total rewards equity growth chart item'
      properties:
        label:
          description: Label
          type: string
        estimatedNetValue:
          description: 'Estimated value'
          type: string
        subvalue:
          description: 'Source amount for estimated value when it was currency-converted'
          oneOf:
            -
              type: string
            -
              type: 'null'
        vested:
          description: 'Amount vested'
          oneOf:
            -
              type: number
              format: float
            -
              type: 'null'
        current:
          description: 'Current value'
          oneOf:
            -
              type: number
              format: float
            -
              type: 'null'
        unvested:
          description: 'Amount unvested'
          oneOf:
            -
              type: number
              format: float
            -
              type: 'null'
      type: object
    TotalRewards-EquityGrowthEstimation:
      description: 'Schema for total rewards equity growth estimation'
      properties:
        stop:
          description: Stop
          type: integer
        companyValuation:
          description: 'Company valuation'
          type: string
        companyValuationSubvalue:
          description: 'Source company valuation when converted'
          oneOf:
            -
              type: string
            -
              type: 'null'
        totalValue:
          description: 'Total value'
          type: string
        totalValueSubvalue:
          description: 'Source total when converted'
          oneOf:
            -
              type: string
            -
              type: 'null'
        vestedValue:
          description: 'Vested amount value'
          type: string
        vestedValueSubvalue:
          description: 'Source vested when converted'
          oneOf:
            -
              type: string
            -
              type: 'null'
      type: object
    TotalRewards-EquitySectionValues:
      description: 'Schema for total rewards equity section values'
      properties:
        compSummary:
          $ref: '#/components/schemas/TotalRewards-CompSummaryValues'
          description: 'Compensation summary values for equity'
        conditions:
          description: 'Terms and conditions'
          oneOf:
            -
              type: string
            -
              type: 'null'
        disclaimers:
          description: Disclaimers
          oneOf:
            -
              type: string
            -
              type: 'null'
        equityDetails:
          description: 'Equity details'
          type: array
          items:
            $ref: '#/components/schemas/TotalRewards-EquityDetailsValues'
        equityEstimatedValuation:
          $ref: '#/components/schemas/TotalRewards-EquityEstimatedValuationValues'
          description: 'Total rewards equity estimated valuation object'
      type: object
    TotalRewards-ExtraPayDetailsValues:
      description: 'Schema for total rewards extra pay details'
      properties:
        name:
          description: Name
          type: string
        amount:
          description: Amount
          type: string
        amountSubvalue:
          description: 'Source amount when amount was currency-converted'
          oneOf:
            -
              type: string
            -
              type: 'null'
        frequency:
          description: Frequency
          type: string
        formattedDateString:
          description: 'Extra pay formatted date'
          type: string
          format: date
        annualizedAmount:
          description: 'Annualized amount'
          type: string
        annualizedSubvalue:
          description: 'Source text when annualized amount was currency-converted'
          oneOf:
            -
              type: string
            -
              type: 'null'
      type: object
    TotalRewards-ExtraPaySectionValues:
      description: 'Schema for total rewards extra pay section values'
      properties:
        title:
          description: Title
          type: string
        compSummary:
          $ref: '#/components/schemas/TotalRewards-CompSummaryValues'
          description: 'Compensation summary values for extra pay'
        extraPayDetails:
          description: 'Extra pay details'
          type: array
          items:
            $ref: '#/components/schemas/TotalRewards-ExtraPayDetailsValues'
      type: object
    TotalRewards-HolidayValue:
      description: 'Schema for total rewards holiday value'
      properties:
        title:
          description: Title
          type: string
        startDate:
          description: 'Start date'
          type: string
        endDate:
          description: 'End date'
          oneOf:
            -
              type: string
            -
              type: 'null'
      type: object
    TotalRewards-OverviewSectionValues:
      description: 'Schema for total rewards overview section'
      properties:
        estimatedTotalCompValue:
          description: 'Estimated total compensation'
          oneOf:
            -
              type: string
            -
              type: 'null'
        paidPer:
          description: 'Paid per reference'
          oneOf:
            -
              type: string
            -
              type: 'null'
        multiplierValue:
          description: 'Multiplier for hours worked for hourly paid type'
          oneOf:
            -
              type: number
              format: float
            -
              type: 'null'
      type: object
    TotalRewards-TimeOffPolicyValue:
      description: 'Schema for total rewards time off policy'
      properties:
        title:
          description: Title
          type: string
        value:
          description: Value
          type: string
        showDisclaimer:
          description: 'Show disclaimer'
          oneOf:
            -
              type: boolean
            -
              type: 'null'
        showEligible:
          description: 'Show eligibility'
          oneOf:
            -
              type: boolean
            -
              type: 'null'
      type: object
    TotalRewards-TimelineItem:
      description: 'Schema for total rewards timeline item'
      properties:
        value:
          description: Value
          oneOf:
            -
              type: string
            -
              type: 'null'
        subvalue:
          description: 'When conversion applies: localized “(from …)” fragment including the original amount, e.g. (from $48,000.00 / Year)'
          oneOf:
            -
              type: string
            -
              type: 'null'
        label:
          description: Label
          oneOf:
            -
              type: string
            -
              type: array
              items:
                type: string
      type: object
    TotalRewards-TimelineSectionValues:
      description: 'Schema for total rewards overview section'
      properties:
        compSummary:
          $ref: '#/components/schemas/TotalRewards-CompSummaryValues'
          description: 'Compensation summary'
        timelineData:
          description: 'Timeline data'
          type: array
          items:
            $ref: '#/components/schemas/TotalRewards-TimelineItem'
        placeholderText:
          description: 'Placeholder text'
          oneOf:
            -
              type: string
            -
              type: 'null'
      type: object
    TotalRewards-EmployeeStatement:
      description: 'Schema for a total rewards statement'
      properties:
        employeeId:
          description: 'Employee Id'
          oneOf:
            -
              type: string
            -
              type: 'null'
        employeeName:
          description: 'Employee Name'
          type: string
        jobTitle:
          description: 'Employee Job Title'
          oneOf:
            -
              type: string
            -
              type: 'null'
        hasMixedCurrencyTypes:
          description: 'Employee statement has multiple currencies'
          oneOf:
            -
              type: boolean
              format: boolean
            -
              type: 'null'
        overviewSection:
          $ref: '#/components/schemas/TotalRewards-OverviewSectionValues'
          description: 'Total Rewards overview section object'
        paySection:
          $ref: '#/components/schemas/TotalRewards-TimelineSectionValues'
          description: 'Total Rewards pay timeline object'
        bonusSection:
          $ref: '#/components/schemas/TotalRewards-TimelineSectionValues'
          description: 'Total Rewards bonus section timeline object'
        commissionSection:
          $ref: '#/components/schemas/TotalRewards-TimelineSectionValues'
          description: 'Total Rewards commission section timeline object'
        benefitSection:
          $ref: '#/components/schemas/TotalRewards-BenefitSectionValues'
          description: 'Total Rewards benefit section object'
        equitySection:
          $ref: '#/components/schemas/TotalRewards-EquitySectionValues'
          description: 'Total Rewards equity section object'
        calendarSection:
          $ref: '#/components/schemas/TotalRewards-CalendarSectionValues'
          description: 'Total Rewards equity section object'
        extraPaySection:
          $ref: '#/components/schemas/TotalRewards-ExtraPaySectionValues'
          description: 'Total Rewards extra pay section object'
        customDisclaimerInfo:
          $ref: '#/components/schemas/TotalRewards-CustomDisclaimerInfo'
          description: 'Total Rewards custom disclaimer object'
        companyName:
          description: 'Company name'
          oneOf:
            -
              type: string
            -
              type: 'null'
        companyLogoUrl:
          description: 'Company logo URL'
          oneOf:
            -
              type: string
            -
              type: 'null'
        statementYear:
          description: 'Statement year'
          oneOf:
            -
              type: integer
            -
              type: 'null'
        minStatementYear:
          description: 'Minimum selectable statement year'
          oneOf:
            -
              type: integer
            -
              type: 'null'
      type: object
    WebHookPostFieldDataObject:
      title: WebHookPostFieldDataObject
      required:
        - alias
        - name
        - type
        - id
        - pageId
        - tableId
      properties:
        alias:
          description: 'Field identifier used in the API'
          oneOf:
            -
              type: string
            -
              type: 'null'
        name:
          description: 'Field name'
          type: string
        type:
          description: 'Field type'
          oneOf:
            -
              type: string
            -
              type: 'null'
        id:
          description: 'Field ID. This may be a standard/custom field ID or a synthetic negative ID for calculated fields.'
          type: integer
        pageId:
          description: 'ID of the page the field belongs to. Calculated fields may use `-1`. Fields without a usable page ID are not available in webhooks.'
          type: integer
        tableId:
          description: 'ID of the table the field belongs to'
          oneOf:
            -
              type: integer
            -
              type: 'null'
      type: object
    WebHookPostFieldDataObjectCollection:
      title: WebHookPostFieldDataObjectCollection
      type: array
      items:
        $ref: '#/components/schemas/WebHookPostFieldDataObject'
    WebHookPostFieldPageDataObject:
      title: WebHookPostFieldPageDataObject
      required:
        - id
        - name
      properties:
        id:
          description: 'Page ID'
          type: integer
        name:
          description: 'Page Name'
          type: string
      type: object
    WebHookPostFieldPageDataObjectCollection:
      title: WebHookPostFieldPageDataObjectCollection
      type: array
      items:
        $ref: '#/components/schemas/WebHookPostFieldPageDataObject'
    WebHookPostFieldResponseObject:
      title: WebHookPostFieldResponseObject
      required:
        - fields
        - tables
        - pages
      properties:
        fields:
          $ref: '#/components/schemas/WebHookPostFieldDataObjectCollection'
          description: 'List of fields that can be included in the webhook payload. Includes standard, custom, table, and calculated fields; some calculated fields use synthetic negative IDs.'
        pages:
          $ref: '#/components/schemas/WebHookPostFieldPageDataObjectCollection'
          description: 'Pages referenced by the postable fields in the webhook payload.'
        tables:
          $ref: '#/components/schemas/WebHookPostFieldTableDataObjectCollection'
          description: 'Tables referenced by the postable fields in the webhook payload.'
      type: object
    WebHookPostFieldTableDataObject:
      title: WebHookPostFieldTableDataObject
      required:
        - id
        - name
      properties:
        id:
          description: 'Table ID'
          type: integer
        name:
          description: 'Table Name'
          type: string
      type: object
    WebHookPostFieldTableDataObjectCollection:
      title: WebHookPostFieldTableDataObjectCollection
      type: array
      items:
        $ref: '#/components/schemas/WebHookPostFieldTableDataObject'
    ProblemDetailsResponse:
      description: 'problem fields shared by problem responses and record-level errors.'
      properties:
        type:
          description: 'Problem type URI identifying the error category'
          type: string
        title:
          description: 'Short, human-readable summary of the problem type'
          type: string
        status:
          description: 'HTTP status code for the error'
          type: integer
        detail:
          description: 'Detailed, human-readable explanation specific to this occurrence of the problem'
          type: string
        instance:
          description: 'URI reference that identifies the specific occurrence of the problem'
          type: string
        code:
          description: 'Application-specific error code'
          type: string
        fields:
          type: object
          example:
            amount: 'must by > 0'
          additionalProperties:
            type: string
      type: object
    ListFieldOption:
      description: 'A single option on a list field.'
      properties:
        id:
          description: 'The option ID.'
          type: integer
          xml:
            attribute: true
        name:
          description: 'The display value of the option. In XML format, this is the text content of the <option> element.'
          type: string
        archived:
          description: 'Whether this option is archived. Archived options are preserved for historical data integrity and continue to appear in responses. Filter by archived: no to show only active options.'
          type: string
          enum:
            - 'yes'
            - 'no'
          xml:
            attribute: true
        createdDate:
          description: 'The date the option was created, in ISO 8601 format. Server-set on creation; not client-supplied.'
          xml:
            attribute: true
          oneOf:
            -
              type: string
            -
              type: 'null'
        archivedDate:
          description: 'The date the option was archived, in ISO 8601 format. Server-set when archived is first set to yes; persists even if the option is later un-archived.'
          xml:
            attribute: true
          oneOf:
            -
              type: string
            -
              type: 'null'
        manageable:
          description: 'Whether this individual option can be modified via the API.'
          type: string
          enum:
            - 'yes'
            - 'no'
          xml:
            attribute: true
        frequency:
          description: 'The pay frequency for the option. Only present on pay-group list fields.'
          xml:
            attribute: true
          oneOf:
            -
              type: string
            -
              type: 'null'
      type: object
      xml:
        name: option
    ListFieldDetail:
      description: 'A single list field and its available options.'
      properties:
        id:
          description: 'The list ID.'
          type: integer
          xml:
            attribute: true
        fieldId:
          description: 'The field ID.'
          type: integer
          xml:
            attribute: true
        name:
          description: 'The display name of the list field.'
          type: string
        alias:
          description: 'The API alias for the list field.'
          xml:
            attribute: true
          oneOf:
            -
              type: string
            -
              type: 'null'
        manageable:
          description: 'Whether the list field options can be modified via the API.'
          type: string
          enum:
            - 'yes'
            - 'no'
          xml:
            attribute: true
        multiple:
          description: 'Whether this field supports multiple values.'
          type: string
          enum:
            - 'yes'
            - 'no'
          xml:
            attribute: true
        options:
          description: 'The available options for this list field.'
          type: array
          items:
            $ref: '#/components/schemas/ListFieldOption'
          xml:
            name: options
            wrapped: true
      type: object
      xml:
        name: list
    AvailableAction:
      description: 'Represents an action that can be performed on a resource based on permissions and resource state'
      properties:
        href:
          description: 'The URL for this action'
          type: string
          example: /api/v1/hris/job-openings/uuid-123
        fields:
          description: 'Fields that can be modified with this action'
          oneOf:
            -
              type: array
              items:
                type: object
            -
              type: 'null'
      type: object
    PagedResponse:
      description: 'A response object that contains a paginated list of results.'
      properties:
        data:
          description: 'The data for the current page.'
          type: array
          items:
            type: object
        meta:
          $ref: '#/components/schemas/PaginationMetaData'
          description: 'The pagination metadata for the response.'
        _links:
          description: 'Hypermedia links for collection navigation (next, prev) and available collection actions (e.g., create). Actions are permission-based and may vary.'
          example:
            next:
              href: '/api/v1/reference-resources?page=2&pageSize=100'
            prev:
              href: '/api/v1/reference-resources?page=0&pageSize=100'
          oneOf:
            -
              type: object
              additionalProperties:
                $ref: '#/components/schemas/AvailableAction'
            -
              type: 'null'
      type: object
    PaginationMetaData:
      description: 'A response object that contains pagination metadata.'
      properties:
        page:
          description: 'The page number of the results.'
          type: integer
          example: 0
        pageSize:
          description: 'The number of items per page.'
          type: integer
          example: 100
        totalPages:
          description: 'The total number of pages available.'
          type: integer
          example: 1
        totalItems:
          description: 'The total number of items available.'
          type: integer
          example: 10
      type: object
  responses:
    '400':
      description: Unauthorized
      headers:
        X-BambooHR-Message:
          description: 'Human readable error to help when debugging, suitable for logging'
          schema:
            type: string
      content:
        application/json: {  }
    '401':
      description: Unauthorized
      headers:
        X-BambooHR-Message:
          description: 'Human readable error to help when debugging, suitable for logging'
          schema:
            type: string
      content:
        application/json: {  }
    '403':
      description: Forbidden
      headers:
        X-BambooHR-Message:
          description: 'Human readable error to help when debugging, suitable for logging'
          schema:
            type: string
      content:
        application/json: {  }
    '404':
      description: 'Not Found'
      headers:
        X-BambooHR-Message:
          description: 'Human readable error to help when debugging, suitable for logging'
          schema:
            type: string
      content:
        application/json: {  }
    '409':
      description: Conflict
      headers:
        X-BambooHR-Message:
          description: 'Human readable error to help when debugging, suitable for logging'
          schema:
            type: string
      content:
        application/json: {  }
    '413':
      description: 'Payload too large'
      headers:
        X-BambooHR-Message:
          description: 'The attempted file upload was too large'
          schema:
            type: string
      content:
        application/json: {  }
    '500':
      description: 'Unknown Error'
      headers:
        X-BambooHR-Message:
          description: 'There was an unknown server error'
          schema:
            type: string
      content:
        application/json: {  }
    BenefitIntegrationSuccessSyncResponse:
      description: 'Human-readable sync results'
      content:
        text/plain:
          schema:
            description: 'The result of the sync operation'
            type: string
    BenefitIntegrationSyncablePlanResponse:
      description: 'List of syncable plans'
      content:
        application/json:
          schema:
            type: array
            items:
              properties:
                id:
                  description: 'Plan ID'
                  type: string
                type:
                  description: 'Plan category'
                  type: string
                name:
                  description: 'Plan name'
                  type: string
                carrierName:
                  description: 'Carrier name'
                  type: string
                syncingCapabilities:
                  description: 'Which integration the plan is capable of being synced with'
                  type: array
                  items: { type: string, enum: [ideon, noyo] }
              type: object
    DependentValidationFailedResponse:
      description: 'Validation failed - returns an object of error messages keyed by the dependent field ID'
      content:
        application/json:
          schema:
            properties:
              empDepFirstName:
                type: string
              empDepLastName:
                type: string
              empDepMiddlename:
                type: string
              empDepAdd1:
                type: string
              empDepAdd2:
                type: string
              empDepCity:
                type: string
              empDepRelationship:
                type: string
              employeeDependentSsn:
                type: string
              employeeDependentSin:
                type: string
              empDepState:
                type: string
              empDepZipCode:
                type: string
              empDepGender:
                type: string
              empDepBirthday:
                type: string
              empDepFTStudent:
                type: string
              empDepUSCitizen:
                type: string
              empDepHomePhone:
                type: string
              empDepCountry:
                type: string
            type: object
            additionalProperties: false
    WebhookSuccessResponse:
      description: 'Webhook received successfully (200-299 status code).'
    WebhookClientErrorResponse:
      description: 'Client error (400-499 status code) - the webhook endpoint intentionally rejected the request. BambooHR will not retry webhooks that return 4xx status codes.'
    WebhookServerErrorResponse:
      description: 'Server error (500+ status code). BambooHR will automatically retry this webhook up to 5 times using exponential backoff (5min, 10min, 20min, 40min, 80min intervals).'
  parameters:
    companyDomain:
      name: companyDomain
      in: path
      description: 'The subdomain used to access BambooHR. If you access BambooHR at https://mycompany.bamboohr.com, then the companyDomain is "mycompany"'
      required: true
      schema:
        type: string
    EmployeeIdPathParameter:
      name: EmployeeIdPathParameter
      in: path
      description: 'The ID of the employee.'
      required: true
      schema:
        type: integer
    AcceptHeaderParameter:
      name: AcceptHeaderParameter
      in: header
      description: 'This endpoint can produce either JSON or XML.'
      schema:
        type: string
        enum:
          - application/xml
          - application/json
    AcceptXmlHeaderParameter:
      name: AcceptXmlHeaderParameter
      in: header
      description: 'This endpoint will produce XML.'
      schema:
        type: string
        enum:
          - application/xml
    AcceptJsonHeaderParameter:
      name: AcceptJsonHeaderParameter
      in: header
      description: 'This endpoint will produce JSON.'
      schema:
        type: string
        enum:
          - application/json
    MagicLinkToken:
      name: token
      in: path
      description: 'One-time magic-link token delivered to the client user via email. 64 lowercase-hex characters — the plaintext form of bin2hex(random_bytes(32)).'
      required: true
      schema:
        type: string
        maxLength: 64
        minLength: 64
        pattern: '^[0-9a-f]{64}$'
    ClientId:
      name: clientId
      in: path
      required: true
      schema:
        type: string
  requestBodies:
    RequestBodyParameter:
      required: true
      content:
        application/json:
          schema:
            type: string
    DependentUpdateRequest:
      required: true
      content:
        application/json:
          schema:
            properties:
              fields:
                type: array
                items:
                  properties: { id: { type: string }, value: { type: string } }
                  type: object
            type: object
  securitySchemes:
    oauth:
      type: oauth2
      description: 'OAuth 2.0 authentication'
      flows:
        authorizationCode:
          authorizationUrl: /authorize.php
          tokenUrl: /token.php
          scopes:
            access_level: 'Get User Access Levels'
            'benchmarking:compensation': 'Compensation Benchmarking'
            compensation_benchmarks: 'Read and write access to compensation benchmarking data including job-location pairs, benchmark values, and benchmark sources.'
            compensation_benchmarks.write: 'Read and write access to compensation benchmarking data including job-location pairs, benchmark values, and benchmark sources.'
            pay_grades_and_bands: 'Read access to levels and bands data including compensation level groups, levels, pay bands, and job title assignments.'
            pay_grades_and_bands.write: 'Read access to levels and bands data including compensation level groups, levels, pay bands, and job title assignments.'
            planning_cycles: 'Read access to compensation planning cycle data including cycle lists, details, summaries, employees, budgets, approvals, admins, and change communication settings.'
            planning_cycles.write: 'Read access to compensation planning cycle data including cycle lists, details, summaries, employees, budgets, approvals, admins, and change communication settings.'
            background_checks: 'Access background check information'
            background_checks.write: 'Access background check information'
            benefit: 'Get Benefit Details'
            benefit.write: 'Get Benefit Details'
            company_file: 'Company Files'
            company_file.write: 'Company Files'
            'company:info': 'Company Info'
            'company:details': 'Access to comprehensive company information including profile fields, metadata, and configuration settings'
            'company:details.write': 'Access to comprehensive company information including profile fields, metadata, and configuration settings'
            customer_onboarding: 'Customer Onboarding workflows and setup'
            customer_onboarding.write: 'Customer Onboarding workflows and setup'
            'company:administration': 'Company Administration'
            'company:administration.write': 'Company Administration'
            data_cleaner: 'Allows viewing data cleaning job data.'
            data_cleaner.write: 'Allows viewing data cleaning job data.'
            deductions: 'Access to employee deductions information'
            deductions.write: 'Access to employee deductions information'
            payroll: 'Allows access to payroll related fields'
            payroll.write: 'Allows access to payroll related fields'
            'payroll:beneficial_owners': 'Access to beneficial ownership certification information for payroll compliance, including business structure, exemption status, and individual beneficial owner details with personal information and ownership percentages'
            'payroll:beneficial_owners.write': 'Access to beneficial ownership certification information for payroll compliance, including business structure, exemption status, and individual beneficial owner details with personal information and ownership percentages'
            'payroll:legal_entities': 'Access to payroll legal entities information, including FEIN and legal entity details'
            'payroll:retirements': 'Access to employee payroll retirements information'
            'payroll:pay_schedules': 'Access to payroll pay schedule information'
            'payroll:pay_periods': 'Access to payroll pay period information'
            'payroll:earnings': 'Access to earnings records within a pay cycle'
            'payroll:pay_cycles': 'Access to pay cycle records'
            'payroll:pay_stubs': 'Access to employee pay stub information including gross pay, net pay, deductions, taxes, and YTD totals'
            'payroll:payrolls': 'Access to payroll information including pay dates and processing details'
            'payroll:ach_returns': 'Access to ACH return information including return details and resolution'
            'payroll:ach_returns.write': 'Access to ACH return information including return details and resolution'
            'esignature:instance': 'Access eSignature instance data and signing workflows, including viewing instances, completing signing steps, and managing user signatures.'
            'esignature:instance.write': 'Access eSignature instance data and signing workflows, including viewing instances, completing signing steps, and managing user signatures.'
            'employee:assets': 'Assets Assigned to Employee'
            'employee:assets.write': 'Assets Assigned to Employee'
            'employee:emergency_contacts': 'Employee Contacts and Emergency Contacts'
            'employee:emergency_contacts.write': 'Employee Contacts and Emergency Contacts'
            'employee:vaccination': 'Employee COVID-19 Data'
            'employee:vaccination.write': 'Employee COVID-19 Data'
            'employee:custom_fields': 'Employee Custom Fields'
            'employee:custom_fields.write': 'Employee Custom Fields'
            'employee:custom_fields_encrypted': 'Employee Encrypted Custom Fields'
            'employee:custom_fields_encrypted.write': 'Employee Encrypted Custom Fields'
            'employee:demographic': 'Includes Employee Ethnicity and Nationality'
            'employee:demographic.write': 'Includes Employee Ethnicity and Nationality'
            'employee:dependent': 'Employee Dependent Information'
            'employee:dependent.write': 'Employee Dependent Information'
            'employee:dependent:ssn': 'Employee Dependent SSN/SIN'
            'employee:dependent:ssn.write': 'Employee Dependent SSN/SIN'
            'employee:education': 'Employee Education Information'
            'employee:education.write': 'Employee Education Information'
            'employee:contact': 'Employee Contact'
            'employee:contact.write': 'Employee Contact'
            'employee:identification': 'Employee Identification'
            'employee:identification.write': 'Employee Identification'
            'employee:job': 'Employee Job Information'
            'employee:job.write': 'Employee Job Information'
            'employee:management': 'Manage Employees'
            'employee:management.write': 'Manage Employees'
            'employee:photo': 'Employee Photo'
            'employee:photo.write': 'Employee Photo'
            employee: 'All non-sensitive employee-related information'
            employee.write: 'All non-sensitive employee-related information'
            'employee:name': 'Includes Any Employee Name Details'
            'employee:name.write': 'Includes Any Employee Name Details'
            employee_directory: 'Employee Directory'
            'employee:file': 'Employee Files'
            'employee:file.write': 'Employee Files'
            'employee:compensation': 'Employee Payroll Information'
            'employee:compensation.write': 'Employee Payroll Information'
            'employee:providers': 'Allows access to employee provider information'
            'employee:providers.write': 'Allows access to employee provider information'
            'employee:providers:payroll': 'Allows access to employee payroll provider information'
            'employee:providers:payroll.write': 'Allows access to employee payroll provider information'
            'employee:tabs': 'View and manage custom employee tabs.'
            'employee:tabs.write': 'View and manage custom employee tabs.'
            'sensitive_employee:protected_info': 'Employee SSN, SIN, Birthday'
            'sensitive_employee:protected_info.write': 'Employee SSN, SIN, Birthday'
            'sensitive_employee:address': 'Includes Any Employee Address Details'
            'sensitive_employee:address.write': 'Includes Any Employee Address Details'
            'sensitive_employee:creditcards': 'Credit Cards Assigned to Employee'
            'sensitive_employee:creditcards.write': 'Credit Cards Assigned to Employee'
            'employee:payroll.write': 'Employee Payroll Information'
            'ask_bamboohr:chat_messages': 'Read and write chat messages in Ask BambooHR on behalf of the authenticated user'
            'ask_bamboohr:chat_messages.write': 'Read and write chat messages in Ask BambooHR on behalf of the authenticated user'
            'ask_bamboohr:settings': 'View and manage Ask BambooHR feature settings'
            'ask_bamboohr:settings.write': 'View and manage Ask BambooHR feature settings'
            'ask_bamboohr:policy_documents': 'View and manage policy documents used as sources in Ask BambooHR'
            'ask_bamboohr:policy_documents.write': 'View and manage policy documents used as sources in Ask BambooHR'
            equity: 'Read access to company-level equity settings such as calculation type, valuation, share counts, pricing, and vesting-related configuration.'
            equity.write: 'Read access to company-level equity settings such as calculation type, valuation, share counts, pricing, and vesting-related configuration.'
            field: 'Query Lists of Fields, Tables and Lists.'
            field.write: 'Query Lists of Fields, Tables and Lists.'
            goal: 'Access performance goals data for employees.'
            goal.write: 'Access performance goals data for employees.'
            'performance:assessments': 'Access performance assessment data for employees'
            'performance:assessments.write': 'Access performance assessment data for employees'
            'performance:feedback': 'Access performance feedback data for employees'
            'performance:feedback.write': 'Access performance feedback data for employees'
            'performance:settings': 'Access and manage performance management configurations'
            'performance:settings.write': 'Access and manage performance management configurations'
            'performance:one_on_ones': 'Access one on one meeting data for employees'
            'performance:one_on_ones.write': 'Access one on one meeting data for employees'
            'enps:settings': 'View and manage eNPS feature settings'
            'enps:settings.write': 'View and manage eNPS feature settings'
            'employee_wellbeing:settings': 'View and manage Employee Wellbeing feature settings'
            'employee_wellbeing:settings.write': 'View and manage Employee Wellbeing feature settings'
            job_organization: 'Manage job organization, including job profiles'
            job_organization.write: 'Manage job organization, including job profiles'
            'hiring:job_openings': 'Access and manage job openings, pipelines, rules, questions, collaborators, and job board postings'
            'hiring:job_openings.write': 'Access and manage job openings, pipelines, rules, questions, collaborators, and job board postings'
            'hiring:applications': 'Access and manage applications, candidates, interviews, and candidate emails'
            'hiring:applications.write': 'Access and manage applications, candidates, interviews, and candidate emails'
            'hiring:offers': 'Access and manage offers, offer terms, and offer letters'
            'hiring:offers.write': 'Access and manage offers, offer terms, and offer letters'
            'hiring:checkr': 'Access and manage Checkr background checks for candidates'
            'hiring:checkr.write': 'Access and manage Checkr background checks for candidates'
            'hiring:talent_pools': 'Access and manage talent pools, pool candidates, and pool collaborators'
            'hiring:talent_pools.write': 'Access and manage talent pools, pool candidates, and pool collaborators'
            'hiring:settings': 'Access and manage hiring configuration including statuses, candidate sources, and email and offer letter templates'
            'hiring:settings.write': 'Access and manage hiring configuration including statuses, candidate sources, and email and offer letter templates'
            offline_access: 'Allows the application to reauthenticate to BambooHR with a refresh token'
            openid: 'Allows the application to authenticate with BambooHR'
            'recognitions:manage': 'Access and manage Rewards and Recognition settings, exclusion rules, and point allocations for the company'
            'recognitions:manage.write': 'Access and manage Rewards and Recognition settings, exclusion rules, and point allocations for the company'
            'recognitions:suggestions': 'Read and write recognition suggestions for employees, including viewer suggestion pools and one-off or broadcast suggestion writes'
            'recognitions:suggestions.write': 'Read and write recognition suggestions for employees, including viewer suggestion pools and one-off or broadcast suggestion writes'
            report: 'Allows access to query custom reports and creation of bulk data requests'
            tasks: 'Manage Employee Tasks'
            tasks.write: 'Manage Employee Tasks'
            time_off: 'Employee Time Off Information'
            time_off.write: 'Employee Time Off Information'
            time_tracking: 'Add and Manage Hours Worked.'
            time_tracking.write: 'Add and Manage Hours Worked.'
            'time_tracking:breaks': 'View and manage meal and rest break tracking information.'
            'time_tracking:breaks.write': 'View and manage meal and rest break tracking information.'
            'time_tracking:shift_differentials': 'View and manage time tracking shift differentials.'
            'time_tracking:shift_differentials.write': 'View and manage time tracking shift differentials.'
            'time_tracking:timesheets': 'View and manage time tracking timesheets.'
            'time_tracking:timesheets.write': 'View and manage time tracking timesheets.'
            'time_tracking:project': 'View and manage time tracking projects and tasks.'
            'time_tracking:project.write': 'View and manage time tracking projects and tasks.'
            'scheduling:schedules': 'View and manage schedules.'
            'scheduling:schedules.write': 'View and manage schedules.'
            'scheduling:shifts': 'View and manage shifts within schedules.'
            'scheduling:shifts.write': 'View and manage shifts within schedules.'
            training: 'Get Training Details'
            training.write: 'Get Training Details'
            total_rewards: 'Read and write access to Total Rewards data including employee statements, onboarding steps, and custom disclaimers.'
            total_rewards.write: 'Read and write access to Total Rewards data including employee statements, onboarding steps, and custom disclaimers.'
            user: 'Get Users List'
            user.write: 'Get Users List'
            'user:management': 'Manage state of company users'
            'user:management.write': 'Manage state of company users'
            webhooks: 'Create and Manage Application Specific Webhooks'
            webhooks.write: 'Create and Manage Application Specific Webhooks'
            'workflows:external_tasks': 'View and manage workflow external tasks.'
            'workflows:external_tasks.write': 'View and manage workflow external tasks.'
            'workflows:instances': 'View and manage workflow instances.'
            'workflows:instances.write': 'View and manage workflow instances.'
            error_management: 'Manage errors and notifications'
            error_management.write: 'Manage errors and notifications'
            public.integration: 'Allows access to all authorized scopes for all employees. Must be installed by an admin.'
            public.user: 'Authorizes the application to perform scoped actions on behalf of the user who authorized the application.'
            alerts: 'Provides read and write access to alert configurations.'
            alerts.write: 'Provides read and write access to alert configurations.'
            approval_workflows: 'Provides read and write access to approval workflow configurations.'
            approval_workflows.write: 'Provides read and write access to approval workflow configurations.'
            compensation_tools: 'Read access to the list of available compensation tools and upsell information for the company.'
            employee_verifications: 'Provides read and write access to employee verification records, including I-9 and E-Verify verification data and partner role assignments.'
            employee_verifications.write: 'Provides read and write access to employee verification records, including I-9 and E-Verify verification data and partner role assignments.'
            esignature: 'Provides read access to electronic signature records and requests.'
            onboarding: 'Provides read access to employee new hire paperwork and onboarding records.'
            onboarding.write: 'Provides read access to employee new hire paperwork and onboarding records.'
            gridlets: Gridlets
            meta: 'Read-only access to public BambooHR meta reference endpoints such as countries, subdivisions, timezones, currencies, and related lookup data used to configure integrations.'
            mcp: 'Grants access to Model Context Protocol (MCP) endpoints'
            insights: 'Insight data'
            insights.write: 'Insight data'
    basic:
      type: http
      scheme: basic
    RegistrationAccessToken:
      type: http
      description: 'Registration access token returned on POST /register and rotated on successful PUT /{clientId}; previous token remains valid for up to one hour for retry recovery.'
      bearerFormat: opaque-token
      scheme: bearer
    Api7RegistrationKey:
      type: apiKey
      description: 'Static key passed in X-API7-REGISTRATION-KEY header.'
      name: X-API7-REGISTRATION-KEY
      in: header
tags:
  -
    name: Benefits
  -
    name: 'Alert Configurations'
    description: 'Alert Configurations'
  -
    name: 'Public API'
    description: 'Public API'
  -
    name: Alert
    description: Alert
  -
    name: Compensation
    description: Compensation
  -
    name: 'Compensation Planning'
    description: 'Compensation Planning'
  -
    name: 'Employee Verification'
    description: 'Employee Verification'
  -
    name: Onboarding
    description: Onboarding
  -
    name: 'Time Tracking'
    description: 'Time Tracking'
  -
    name: Scheduling
    description: Scheduling
  -
    name: 'Time Off'
    description: 'Time Off'
  -
    name: 'Meal & Rest Breaks'
    description: 'Meal & Rest Breaks'
  -
    name: Webhooks
    description: Webhooks
  -
    name: Datasets
    description: Datasets
  -
    name: 'Custom Reports'
    description: 'Custom Reports'
  -
    name: 'Applicant Tracking'
    description: 'Applicant Tracking'
  -
    name: Company
    description: Company
  -
    name: Employees
    description: Employees
  -
    name: 'Company Profile'
    description: 'Company Profile'
  -
    name: 'Compensation Benchmarking'
    description: 'Compensation Benchmarking'
  -
    name: Reports
    description: Reports
  -
    name: 'Employee Tables'
    description: 'Employee Tables'
  -
    name: 'Company Files'
    description: 'Company Files'
  -
    name: 'Employee Files'
    description: 'Employee Files'
  -
    name: Goals
    description: Goals
  -
    name: Photos
    description: Photos
  -
    name: Locations
    description: Locations
  -
    name: Hours
    description: Hours
  -
    name: 'Total Rewards'
    description: 'Total Rewards'
  -
    name: 'Account Information'
    description: 'Account Information'
  -
    name: Training
    description: Training
  -
    name: 'Change Tracking'
    description: 'Change Tracking'
  -
    name: Login
    description: Login
webhooks:
  company-integrations.updated:
    post:
      tags:
        - 'Webhook Events'
        - 'Public API'
      summary: 'Company Integrations Updated'
      description: "\nTriggered when company integrations are updated.\n\n### Behavior & Constraints\n- **Permission Checking**: This event does **not** enforce permission checking. The webhook will fire for all subscribers regardless of the API token's permissions.\n- **Event Scope**: This event fires when any integration settings are modified for the company, including enabling, disabling, or updating integration configurations.\n"
      operationId: company-integrations-updated-webhook
      requestBody:
        description: 'Webhook payload containing company integration update information'
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CompanyIntegrationsUpdatedWebhookPayload'
      responses:
        '200':
          $ref: '#/components/responses/WebhookSuccessResponse'
        '400':
          $ref: '#/components/responses/WebhookClientErrorResponse'
        '500':
          $ref: '#/components/responses/WebhookServerErrorResponse'
      x-team-name: plat-api
  company.updated:
    post:
      tags:
        - 'Webhook Events'
        - 'Public API'
      summary: 'Company Updated'
      description: "\nTriggered when company information or company status changes.\n\n### Behavior & Constraints\n- This event is emitted for general company updates.\n- When the underlying change is a company status change, the emitted event depends on the new status:\n  - If the new status is `DELETED` or `CANCELLED`, the system emits `company.deleted` instead.\n  - Otherwise the system emits `company.updated`.\n"
      operationId: company-updated-webhook
      requestBody:
        description: 'Webhook payload containing information about the company update'
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CompanyUpdatedWebhookPayload'
      responses:
        '200':
          $ref: '#/components/responses/WebhookSuccessResponse'
        '400':
          $ref: '#/components/responses/WebhookClientErrorResponse'
        '500':
          $ref: '#/components/responses/WebhookServerErrorResponse'
      x-team-name: grease-monkeys
  company.deleted:
    post:
      tags:
        - 'Webhook Events'
        - 'Public API'
      summary: 'Company Deleted'
      description: "\nTriggered when a company status change results in the company being considered deleted/closed.\n\n### Behavior & Constraints\n- This event is emitted when the new company status is `DELETED` or `CANCELLED`.\n- For other company updates and status changes, the system emits `company.updated`.\n"
      operationId: company-deleted-webhook
      requestBody:
        description: 'Webhook payload containing information about the company deletion'
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CompanyDeletedWebhookPayload'
      responses:
        '200':
          $ref: '#/components/responses/WebhookSuccessResponse'
        '400':
          $ref: '#/components/responses/WebhookClientErrorResponse'
        '500':
          $ref: '#/components/responses/WebhookServerErrorResponse'
      x-team-name: grease-monkeys
  employee.created:
    post:
      tags:
        - 'Webhook Events'
        - 'Public API'
      summary: 'Employee Created'
      description: "\nTriggered when a new employee is created.\n\n### Behavior & Constraints\n- **Creation Sequence**: When an employee is created, both `employee.created` and `employee.updated` events fire sequentially due to the create-then-initialize pattern.\n- **Data Availability**: The `data` object contains the `companyId` and `employeeId` of the newly created employee.\n- **Permission Checking**: This event does **not** enforce permission checking. The webhook will fire for all subscribers regardless of the API token's field-level permissions.\n\n### Deprecation Notice\n**Note:** This event is the modern replacement for the legacy `employee_with_fields.created` event. While the legacy event is deprecated, it will remain available for the foreseeable future to support existing integrations. We encourage using this new event for all new development.\n\n**Important:** You cannot subscribe to both this new event and the legacy `employee_with_fields` events on the same webhook. Webhooks created without specifying any events will default to the legacy behavior, effectively subscribing to the `employee_with_fields` events automatically.\n"
      operationId: employee-created-webhook
      requestBody:
        description: 'Webhook payload containing information about the employee creation'
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EmployeeCreatedWebhookPayload'
      responses:
        '200':
          $ref: '#/components/responses/WebhookSuccessResponse'
        '400':
          $ref: '#/components/responses/WebhookClientErrorResponse'
        '500':
          $ref: '#/components/responses/WebhookServerErrorResponse'
      x-team-name: plat-api
  employee.deleted:
    post:
      tags:
        - 'Webhook Events'
        - 'Public API'
      summary: 'Employee Deleted'
      description: "\nTriggered when an employee is deleted.\n\n### Behavior & Constraints\n- **Permission Checking**: This event does **not** enforce permission checking. The webhook will fire for all subscribers regardless of the API token's field-level permissions.\n\n### Deprecation Notice\n**Note:** This event is the modern replacement for the legacy `employee_with_fields.deleted` event. While the legacy event is deprecated, it will remain available for the foreseeable future to support existing integrations. We encourage using this new event for all new development.\n\n**Important:** You cannot subscribe to both this new event and the legacy `employee_with_fields` events on the same webhook. Webhooks created without specifying any events will default to the legacy behavior, effectively subscribing to the `employee_with_fields` events automatically.\n"
      operationId: employee-deleted-webhook
      requestBody:
        description: 'Webhook payload containing information about the employee deletion'
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EmployeeDeletedWebhookPayload'
      responses:
        '200':
          $ref: '#/components/responses/WebhookSuccessResponse'
        '400':
          $ref: '#/components/responses/WebhookClientErrorResponse'
        '500':
          $ref: '#/components/responses/WebhookServerErrorResponse'
      x-team-name: plat-api
  employee.updated:
    post:
      tags:
        - 'Webhook Events'
        - 'Public API'
      summary: 'Employee Updated'
      description: "\nTriggered when an employee record is updated and at least one of the monitored fields has changed.\n\n### Behavior & Constraints\n- **Monitoring**: At least one `monitorField` is required when subscribing to this event via `POST /api/v1/webhooks`.\n- **Permissions**: For API-created webhooks, this event will not fire if the webhook creator lacks access to all monitored fields being changed.\n\n### Detailed Description\n- **Creation Sequence**: When an employee is created, both `employee.created` and `employee.updated` events fire sequentially due to the create-then-initialize pattern.\n- **Field Consolidation**: When multiple fields change simultaneously, they may be consolidated into a single event. Currently, custom field updates and standard field updates are grouped separately and may fire as two events.\n- **Effective Dates**: For history-tracked fields (e.g., `jobTitle`, `payRate`), events fire only when changes take effect, not when future-dated changes are created.\n- **Changed Fields**: The `changedFields` array contains the API aliases for fields that changed and are monitored by this webhook. Aliases match those returned by `GET /api/v1/webhooks/monitor_fields`.\n\n### Deprecation Notice\n**Note:** This event is the modern replacement for the legacy `employee_with_fields.updated` event. While the legacy event is deprecated, it will remain available for the foreseeable future to support existing integrations. We encourage using this new event for all new development.\n\n**Important:** You cannot subscribe to both this new event and the legacy `employee_with_fields` events on the same webhook. Webhooks created without specifying any events will default to the legacy behavior, effectively subscribing to the `employee_with_fields` events automatically.\n"
      operationId: employee-updated-webhook
      requestBody:
        description: 'Webhook payload containing information about the employee update'
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EmployeeUpdatedWebhookPayload'
      responses:
        '200':
          $ref: '#/components/responses/WebhookSuccessResponse'
        '400':
          $ref: '#/components/responses/WebhookClientErrorResponse'
        '500':
          $ref: '#/components/responses/WebhookServerErrorResponse'
      x-team-name: plat-api
