openapi: 3.1.0
info:
  title: Fennoa API
  description: >
    ⚠️ **Preview Notice:** This API is not live yet. This documentation is for
    preview purposes only.


    Everything you need to integrate Fennoa into your own systems. Read and
    manage payroll data, employees, and more through a simple REST API.
  version: 2.0.0
servers:
  - url: https://api.fennoa.com/v2
    description: Production server
  - url: https://api.sandbox.fennoa.com/v2
    description: Sandbox
security:
  - OAuth2: []
  - OAuth2Sandbox: []
tags:
  - name: Authentication
    description: >
      ### OAuth 2.0 Client Credentials


      This API uses the **OAuth 2.0 Client Credentials** grant for
      authentication.


      1. Obtain an access token by sending a `POST` request to the appropriate
      token endpoint with your `client_id` and `client_secret`:
         - **Production:** `https://api.fennoa.com/oauth/token`
         - **Sandbox:** `https://api.sandbox.fennoa.com/oauth/token`
      2. Include the access token in the `Authorization` header of all
      subsequent requests:


      ### Required Headers


      All API requests must include the following headers:


      | Header           | Value              |
      Description                          |

      |------------------|--------------------|--------------------------------------|

      | `Authorization`  | `Bearer <token>`   | OAuth 2.0 access
      token               |

      | `X-Company-Code` | `<company_code>`   | Identifies the target
      company        |

      | `Accept`         | `application/json` | Response content
      type                |
  - name: General
  - name: Payroll
  - name: Dimensions
paths:
  /status:
    get:
      summary: Get API status
      operationId: getStatus
      tags:
        - General
      parameters:
        - name: include
          in: query
          description: >
            Comma-separated list of related resources to include in the
            response. Supported values: `scheduled_maintenances`.
          schema:
            type: string
            enum:
              - scheduled_maintenances
      responses:
        '200':
          $ref: '#/components/responses/GetStatus'
        '500':
          $ref: '#/components/responses/InternalError'
  /payroll/employees:
    get:
      summary: List employees
      operationId: listPayrollEmployees
      tags:
        - Payroll
      parameters:
        - name: filter[employment_status]
          in: query
          description: Filter employees by employment status.
          schema:
            type: string
            enum:
              - active
        - name: include
          in: query
          description: >
            Comma-separated list of related resources to include in the
            response. Supported values: `active_employment.employment_group`.
          schema:
            type: string
            enum:
              - active_employment.employment_group
      responses:
        '200':
          $ref: '#/components/responses/ListPayrollEmployees'
    post:
      summary: Create employee
      operationId: createPayrollEmployee
      tags:
        - Payroll
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PayrollEmployeeCreate'
      responses:
        '201':
          $ref: '#/components/responses/GetPayrollEmployee'
  /payroll/employees/{id}:
    get:
      summary: Get payroll employee
      operationId: getPayrollEmployee
      tags:
        - Payroll
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          $ref: '#/components/responses/GetPayrollEmployee'
  /dimension-types:
    post:
      summary: Create dimension type
      operationId: createDimensionType
      tags:
        - Dimensions
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DimensionTypeCreate'
      responses:
        '201':
          $ref: '#/components/responses/GetDimensionType'
        '500':
          $ref: '#/components/responses/InternalError'
    get:
      summary: List dimension types
      operationId: listDimensionTypes
      tags:
        - Dimensions
      responses:
        '200':
          $ref: '#/components/responses/ListDimensionTypes'
        '500':
          $ref: '#/components/responses/InternalError'
  /dimension-types/{id}:
    patch:
      summary: Update dimension type
      operationId: updateDimensionType
      tags:
        - Dimensions
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DimensionTypeUpdate'
      responses:
        '200':
          $ref: '#/components/responses/GetDimensionType'
        '500':
          $ref: '#/components/responses/InternalError'
    get:
      summary: Get dimension type
      operationId: getDimensionType
      tags:
        - Dimensions
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          $ref: '#/components/responses/GetDimensionType'
        '500':
          $ref: '#/components/responses/InternalError'
  /dimension-types/{id}/dimensions:
    get:
      summary: List dimensions for a dimension type
      operationId: listDimensionTypeDimensions
      tags:
        - Dimensions
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: include
          in: query
          description: >
            Comma-separated list of related resources to include in the
            response. Supported values: `dimension_type`.
          schema:
            type: string
            enum:
              - dimension_type
      responses:
        '200':
          $ref: '#/components/responses/ListDimensionTypeDimensions'
        '500':
          $ref: '#/components/responses/InternalError'
  /dimensions:
    post:
      summary: Create dimension
      operationId: createDimension
      tags:
        - Dimensions
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DimensionCreate'
      responses:
        '201':
          $ref: '#/components/responses/GetDimension'
        '500':
          $ref: '#/components/responses/InternalError'
  /dimensions/{id}:
    get:
      summary: Get dimension
      operationId: getDimension
      tags:
        - Dimensions
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
        - name: include
          in: query
          description: >
            Comma-separated list of related resources to include in the
            response. Supported values: `dimension_type`.
          schema:
            type: string
            enum:
              - dimension_type
      responses:
        '200':
          $ref: '#/components/responses/GetDimensionWithDimensionType'
        '500':
          $ref: '#/components/responses/InternalError'
    patch:
      summary: Update dimension
      operationId: updateDimension
      tags:
        - Dimensions
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/DimensionUpdate'
      responses:
        '200':
          $ref: '#/components/responses/GetDimension'
        '500':
          $ref: '#/components/responses/InternalError'
components:
  securitySchemes:
    OAuth2:
      type: oauth2
      description: >
        Use this for the **production** environment.


        You need to additionally provide the `X-Company-Code` header with the
        company code of the target company in every request.
      flows:
        clientCredentials:
          tokenUrl: https://api.fennoa.com/oauth/token
          scopes: {}
    OAuth2Sandbox:
      type: oauth2
      description: >
        Use this for the **sandbox** environment.


        You need to additionally provide the `X-Company-Code` header with the
        company code of the target company in every request.
      flows:
        clientCredentials:
          tokenUrl: https://api.sandbox.fennoa.com/oauth/token
          scopes: {}
  schemas:
    ScheduledMaintenance:
      type: object
      properties:
        service:
          type: string
          example: fennoa
        description:
          type: object
          additionalProperties:
            type: string
          example:
            en: Description in english
        starts_at:
          type: string
          example: '2030-06-12 13:00:00'
        ends_at:
          type: string
          example: '2030-06-12 14:00:00'
    PayrollEmployeeSimple:
      type: object
      properties:
        id:
          type: integer
          description: Identifier of the employee in Fennoa.
          example: 123
        person_number:
          type: string
          description: User modifiable employee identifier in Fennoa.
          example: P1005
        first_name:
          type: string
          example: Veera
        last_name:
          type: string
          example: Virtanen
        identity_number:
          type:
            - string
            - 'null'
          example: 010101-900P
        active_employment:
          type:
            - object
            - 'null'
          description: >
            The employee's current active employment. Only included when
            requested via the `include=active_employment` query parameter. Null
            if the employee has no active employment.
          properties:
            id:
              type: integer
              description: Identifier of the employment.
              example: 789
            employment_group:
              type: object
              properties:
                id:
                  type: integer
                  example: 2
                name:
                  type: string
                  minLength: 1
                  maxLength: 100
                  example: Tuntipalkat
    NextCursor:
      type:
        - string
        - 'null'
      description: >-
        Base64-encoded cursor for the next page, or null when there is no next
        page.
      example: eyJpZCI6MX0=
    Address:
      type: object
      required:
        - street
        - city
        - postal_code
        - country
      properties:
        street:
          type: string
          maxLength: 100
          example: Esimerkkikatu 1
        postal_code:
          type: string
          maxLength: 10
          example: '00100'
        city:
          type: string
          maxLength: 50
          example: Helsinki
        country:
          type: string
          maxLength: 2
          description: ISO 3166-1 alpha-2 country code
          example: FI
    PayrollEmployeeCreate:
      type: object
      required:
        - person_number
        - first_name
        - last_name
        - address
      properties:
        person_number:
          type: string
          maxLength: 20
          description: User modifiable employee identifier in Fennoa.
          example: P1005
        first_name:
          type: string
          maxLength: 100
          example: Veera
        last_name:
          type: string
          maxLength: 100
          example: Virtanen
        date_of_birth:
          type: string
          format: date
        identity_number:
          type: string
          maxLength: 15
          example: 010101-900P
        tax_number:
          type: integer
          minLength: 12
          maxLength: 12
          example: 100012345678
        email:
          type: string
          format: email
          maxLength: 100
          example: vvirtanen@example.com
        phone:
          type: string
          minLength: 3
          maxLength: 20
          example: '+35844123456'
        address:
          allOf:
            - $ref: '#/components/schemas/Address'
        hire_date:
          type: string
          format: date
        employment_group_id:
          type: integer
          example: 3
        payroll_delivery_method:
          type: string
          enum:
            - no_delivery
            - email
            - esalary
          example: esalary
        iban:
          type: string
          example: FI21 1234 5600 0007 85
        bic:
          type: string
          example: NDEAFIHH
        locale:
          type: string
          enum:
            - fi
            - sv
            - en
          description: ISO 639-1 language code
          example: fi
        international_situations:
          type: object
          properties:
            foreign_person_id:
              type: string
              example: M1234567A
            tin:
              type: string
              description: Tax Identification Number
              example: 000-00-0000
            gender:
              type: string
              enum:
                - male
                - female
                - other
              example: female
            insured_in_finland:
              type: boolean
            a1_certificate_id:
              type: integer
              enum:
                - 1
                - 2
                - 3
              description: >
                A1 certificate / social security situation:

                - `1` From Finland A1 certificate or agreement

                - `2` To Finland A1 certificate or agreement

                - `3` Non-EU/EEA country/non-social security agreement country
                (third country)
              example: 2
            has_non_resident_tax_liability:
              type: boolean
            work_address:
              allOf:
                - $ref: '#/components/schemas/Address'
    PayrollEmployee:
      type: object
      properties:
        id:
          type: integer
          description: Identifier of the employee in Fennoa.
          example: 123
        person_number:
          type: string
          description: User modifiable employee identifier in Fennoa.
          example: P1005
        first_name:
          type: string
          example: Veera
        last_name:
          type: string
          example: Virtanen
        date_of_birth:
          type:
            - string
            - 'null'
          format: date
        identity_number:
          type:
            - string
            - 'null'
          example: 010101-900P
        tax_number:
          type:
            - integer
            - 'null'
          example: 100012345678
        email:
          type:
            - string
            - 'null'
          example: vvirtanen@example.com
        phone:
          type:
            - string
            - 'null'
          example: '+35844123456'
        address:
          allOf:
            - $ref: '#/components/schemas/Address'
        hire_date:
          type:
            - string
            - 'null'
          format: date
        employment_group_id:
          type:
            - integer
            - 'null'
          description: ID of the employment group the employee belongs to.
          example: 3
        payroll_delivery_method:
          type: string
          enum:
            - no_delivery
            - email
            - esalary
          example: esalary
        iban:
          type:
            - string
            - 'null'
          example: FI2112345600000785
        bic:
          type:
            - string
            - 'null'
          example: NDEAFIHH
        locale:
          type:
            - string
            - 'null'
          enum:
            - fi
            - sv
            - en
            - null
          description: ISO 639-1 language code
          example: fi
        international_situations:
          type: object
          properties:
            foreign_person_id:
              type:
                - string
                - 'null'
              example: M1234567A
            tin:
              type:
                - string
                - 'null'
              description: Tax Identification Number
              example: 000-00-0000
            gender:
              type:
                - string
                - 'null'
              enum:
                - male
                - female
                - other
                - null
              example: female
            insured_in_finland:
              type: boolean
            a1_certificate_id:
              type:
                - integer
                - 'null'
              enum:
                - 1
                - 2
                - 3
                - null
              description: >
                A1 certificate / social security situation:

                - `1` From Finland A1 certificate or agreement

                - `2` To Finland A1 certificate or agreement

                - `3` Non-EU/EEA country/non-social security agreement country
                (third country)
              example: 2
            has_non_resident_tax_liability:
              type: boolean
            work_address:
              oneOf:
                - $ref: '#/components/schemas/Address'
                - type: 'null'
    DimensionType:
      type: object
      required:
        - id
        - name
        - description
        - active
      properties:
        id:
          type: integer
          description: Identifier of the dimension type.
          example: 1
        name:
          type: object
          description: Localized name of the dimension type.
          required:
            - fi
            - en
            - sv
          properties:
            fi:
              type: string
              example: Projekti
            en:
              type: string
              example: Project
            sv:
              type: string
              example: Projekt
        description:
          type: object
          description: Localized description of the dimension type.
          required:
            - fi
          properties:
            fi:
              type: string
              example: Esimerkkiprojekti
        active:
          type: boolean
          description: Indicates whether the dimension type is in use.
          example: true
    DimensionTypeCreate:
      type: object
      required:
        - name
      properties:
        name:
          type: object
          description: Localized name of the dimension type.
          required:
            - fi
          properties:
            fi:
              type: string
              example: Projekti
            en:
              type: string
              example: Project
            sv:
              type: string
              example: Projekt
        description:
          type: object
          description: Localized description of the dimension type.
          properties:
            fi:
              type: string
              example: Esimerkkiprojekti
        active:
          type: boolean
          description: Indicates whether the dimension type is in use.
          example: true
    DimensionTypeUpdate:
      type: object
      properties:
        name:
          type: object
          description: Localized name of the dimension type.
          properties:
            fi:
              type: string
              example: Projekti
            en:
              type: string
              example: Project
            sv:
              type: string
              example: Projekt
        description:
          type: object
          description: Localized description of the dimension type.
          properties:
            fi:
              type: string
              example: Esimerkkiprojekti
        active:
          type: boolean
          description: Indicates whether the dimension type is in use.
          example: true
    Dimension:
      type: object
      required:
        - id
        - dimension_type_id
        - code
        - name
        - description
        - active
        - valid_from
        - valid_to
      properties:
        id:
          type: integer
          description: Identifier of the dimension.
          example: 10
        dimension_type_id:
          type: integer
          description: ID of the dimension type this dimension belongs to.
          example: 1
        code:
          type: string
          description: User-defined code for the dimension.
          example: CC-001
        name:
          type: object
          description: Localized name of the dimension.
          required:
            - fi
          properties:
            fi:
              type: string
              example: Kustannuspaikka 1
            en:
              type: string
              example: Cost center 1
            sv:
              type: string
              example: Kostnadsställe 1
        description:
          type: object
          description: Localized description of the dimension.
          required:
            - fi
          properties:
            fi:
              type: string
              example: Esimerkkikustannuspaikka
        active:
          type: boolean
          description: Indicates whether the dimension is in use.
          example: true
        valid_from:
          type:
            - string
            - 'null'
          format: date
          description: Date from which the dimension is valid.
          example: '2024-01-01'
        valid_to:
          type:
            - string
            - 'null'
          format: date
          description: Date until which the dimension is valid.
          example: '2024-12-31'
    DimensionWithDimensionType:
      allOf:
        - $ref: '#/components/schemas/Dimension'
        - type: object
          required:
            - dimension_type
          properties:
            dimension_type:
              $ref: '#/components/schemas/DimensionType'
    DimensionCreate:
      type: object
      required:
        - dimension_type_id
        - code
        - name
      properties:
        dimension_type_id:
          type: integer
          description: ID of the dimension type this dimension belongs to.
          example: 1
        code:
          type: string
          description: User-defined code for the dimension.
          maxLength: 16
          example: CC-001
        name:
          type: object
          description: Localized name of the dimension.
          required:
            - fi
          properties:
            fi:
              type: string
              maxLength: 50
              example: Kustannuspaikka 1
            en:
              type: string
              maxLength: 50
              example: Cost center 1
            sv:
              type: string
              maxLength: 50
              example: Kostnadsställe 1
        description:
          type: object
          description: Localized description of the dimension.
          properties:
            fi:
              type: string
              maxLength: 250
              example: Esimerkkikustannuspaikka
        active:
          type: boolean
          description: Indicates whether the dimension is in use. Defaults to `true`.
          example: true
        valid_from:
          type:
            - string
            - 'null'
          format: date
          example: '2024-01-01'
        valid_to:
          type:
            - string
            - 'null'
          format: date
          example: '2024-12-31'
    DimensionUpdate:
      type: object
      properties:
        dimension_type_id:
          type: integer
          description: ID of the dimension type this dimension belongs to.
          example: 1
        code:
          type: string
          description: User-defined code for the dimension.
          maxLength: 16
          example: CC-001
        name:
          type: object
          description: Localized name of the dimension.
          properties:
            fi:
              type: string
              maxLength: 50
              example: Kustannuspaikka 1
            en:
              type: string
              maxLength: 50
              example: Cost center 1
            sv:
              type: string
              maxLength: 50
              example: Kostnadsställe 1
        description:
          type: object
          description: Localized description of the dimension.
          properties:
            fi:
              type: string
              maxLength: 500
              example: Esimerkkikustannuspaikka
        active:
          type: boolean
          description: Indicates whether the dimension is in use.
          example: true
        valid_from:
          type:
            - string
            - 'null'
          format: date
          example: '2024-01-01'
        valid_to:
          type:
            - string
            - 'null'
          format: date
          example: '2024-12-31'
  responses:
    GetStatus:
      description: API status.
      content:
        application/json:
          schema:
            oneOf:
              - title: Default
                type: object
                properties:
                  data:
                    type: object
                    example: {}
                required:
                  - data
              - title: With scheduled maintenances
                type: object
                properties:
                  data:
                    type: object
                    properties:
                      scheduled_maintenances:
                        type: array
                        items:
                          $ref: '#/components/schemas/ScheduledMaintenance'
                    required:
                      - scheduled_maintenances
                required:
                  - data
    InternalError:
      description: Internal server error.
      content:
        application/json:
          schema:
            type: object
            properties:
              code:
                type: string
                example: internal_error
              message:
                type: string
                example: An unexpected error occurred.
            required:
              - code
              - message
    ListPayrollEmployees:
      description: List of employees.
      content:
        application/json:
          schema:
            type: object
            properties:
              data:
                type: array
                items:
                  $ref: '#/components/schemas/PayrollEmployeeSimple'
              meta:
                type: object
                properties:
                  next_cursor:
                    $ref: '#/components/schemas/NextCursor'
    GetPayrollEmployee:
      description: Get employee.
      content:
        application/json:
          schema:
            type: object
            properties:
              data:
                $ref: '#/components/schemas/PayrollEmployee'
    ListDimensionTypes:
      description: List of dimension types.
      content:
        application/json:
          schema:
            type: object
            properties:
              data:
                type: array
                items:
                  $ref: '#/components/schemas/DimensionType'
              meta:
                type: object
                properties:
                  next_cursor:
                    $ref: '#/components/schemas/NextCursor'
    GetDimensionType:
      description: A dimension type.
      content:
        application/json:
          schema:
            type: object
            properties:
              data:
                $ref: '#/components/schemas/DimensionType'
    ListDimensionTypeDimensions:
      description: List of dimensions for the given dimension type.
      content:
        application/json:
          schema:
            type: object
            properties:
              data:
                type: array
                items:
                  $ref: '#/components/schemas/DimensionWithDimensionType'
              meta:
                type: object
                properties:
                  next_cursor:
                    $ref: '#/components/schemas/NextCursor'
    GetDimension:
      description: A dimension.
      content:
        application/json:
          schema:
            type: object
            properties:
              data:
                $ref: '#/components/schemas/Dimension'
    GetDimensionWithDimensionType:
      description: >
        A dimension with its related dimension type. Returned when
        `include=dimension_type` is requested.
      content:
        application/json:
          schema:
            type: object
            properties:
              data:
                $ref: '#/components/schemas/DimensionWithDimensionType'
