> ## Documentation Index
> Fetch the complete documentation index at: https://docs.flowpayroll.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Send a CSV of changes to existing employees

> Needs permission employee:write, or any of employee-payroll:write, working-pattern:write.

One row per employee, found by the `id` column (the `Employee ID` the employee export leads with); the other columns are employee fields, by their path or by `columns`. Only cells that differ from the employee now are changed, and an empty cell leaves the field as it is. Nothing changes yet: the file is compared in the background, and `GET /employees/imports/{importId}` shows the preview once its status is `Ready`. At most 10,000 rows and 5 MB.



## OpenAPI

````yaml /api-reference/specs/pay-api.yaml post /employees/imports
openapi: 3.0.3
info:
  title: FlowPayroll API
  description: >-
    API for payroll management, including payroll upload, lock, HMRC submission,
    employee management
  version: 0.1.0
servers:
  - url: https://api.sandbox.flowpayroll.ai/v1
security:
  - XAuthToken: []
    XOrgId: []
tags:
  - name: Employees
    description: >-
      Create, retrieve, update, and delete employee records, and look them up by
      NI number or payroll ID.
  - name: Starters & leavers
    description: Set and clear starter and leaver details, individually or in bulk.
  - name: Tax codes
    description: >-
      Manage employee tax codes and query the effective tax code and NI category
      on a given date.
  - name: NI & identifiers
    description: Manage NI categories, National Insurance numbers, and payroll IDs.
  - name: Student loans
    description: Start and end student and postgraduate loans.
  - name: Payroll setup
    description: Assign an employee's payroll config and set opening balances.
  - name: Salary
    description: >-
      The employee's effective-dated annual salary history: read it, add or
      correct a version from a date, and remove one. The Salary API pro-rates
      this history onto each pay run.
  - name: Payroll
  - name: Payroll lines
  - name: PayrollConfig
  - name: Pay Elements
  - name: Calculator
  - name: Payslip
  - name: RTI
  - name: YearEnd
  - name: Organisation
paths:
  /employees/imports:
    post:
      tags:
        - Employees
      summary: Send a CSV of changes to existing employees
      description: >-
        Needs permission employee:write, or any of employee-payroll:write,
        working-pattern:write.


        One row per employee, found by the `id` column (the `Employee ID` the
        employee export leads with); the other columns are employee fields, by
        their path or by `columns`. Only cells that differ from the employee now
        are changed, and an empty cell leaves the field as it is. Nothing
        changes yet: the file is compared in the background, and `GET
        /employees/imports/{importId}` shows the preview once its status is
        `Ready`. At most 10,000 rows and 5 MB.
      operationId: StartEmployeeImport
      parameters:
        - $ref: '#/components/parameters/Request-Id'
      requestBody:
        description: The file, and which field each heading holds
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/EmployeeImportRequestDto'
      responses:
        '202':
          description: >-
            File received; the preview is being prepared. `Location` is the
            import, `Retry-After` how soon to look; the organisation's open apps
            also hear `employeeImportChanged` over the websocket.
          content:
            application/json:
              schema:
                type: object
                allOf:
                  - $ref: '#/components/schemas/ResponseBody'
                  - properties:
                      content:
                        type: object
                        properties:
                          data:
                            $ref: '#/components/schemas/EmployeeImportDto'
        '400':
          description: The file cannot be imported as sent; the message says why
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResponseBody'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResponseBody'
        '403':
          description: Forbidden
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResponseBody'
        '500':
          description: An internal server error occurred
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResponseBody'
        '503':
          description: Service Unavailable
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResponseBody'
components:
  parameters:
    Request-Id:
      name: Request-Id
      in: header
      required: false
      description: >-
        Optional. Your id for this request: it is carried into every log line
        about it, so quote it when you contact support.
      schema:
        type: string
  schemas:
    EmployeeImportRequestDto:
      type: object
      description: A CSV of changes to existing employees, one row per employee.
      required:
        - csv
      properties:
        csv:
          type: string
          description: >-
            The file's text. Its first row is the headings, and one column must
            be `id` (the `Employee ID` the export leads with). Only the cells
            that differ from the employee now are changed; an empty cell leaves
            the field as it is.
        columns:
          type: array
          items:
            $ref: '#/components/schemas/EmployeeImportColumnDto'
          nullable: true
          description: >-
            Which field each heading holds, for headings that are not a field's
            path. Headings that are a field's path need no entry.
    ResponseBody:
      type: object
      required:
        - message
      properties:
        message:
          $ref: '#/components/schemas/MessageWithTokenResponse'
          description: >-
            A readable message about the outcome and a stable token a client can
            branch on.
        content:
          type: object
          required:
            - metadata
          properties:
            data:
              description: >-
                The resource or list the request asked for; left out when there
                is none.
            metadata:
              type: object
              required:
                - dateFormat
                - dateTimeFormat
              properties:
                dateFormat:
                  type: string
                  description: The format of the dates in this response, yyyy-MM-dd.
                dateTimeFormat:
                  type: string
                  description: >-
                    The format of the timestamps in this response,
                    yyyy-MM-ddTHH:mm:ss.fffZ in UTC.
                paginationToken:
                  type: string
                  nullable: true
                  description: >-
                    Send this value back as the paginationToken query parameter
                    to get the next page; absent when there are no more pages.
              description: >-
                The date formats the response uses and, on a paged list, the
                token for the next page.
          description: >-
            The data the request returns with its metadata; left out on a
            response that carries no data, such as most errors.
        validationIssues:
          type: array
          items:
            $ref: '#/components/schemas/ValidationIssue'
          nullable: true
          description: >-
            Each field the request was refused for and why; left out when the
            request was not refused for its fields.
    EmployeeImportDto:
      type: object
      description: >-
        A bulk update of employees from a file: its preview, then how applying
        it went. Only the person who started it can read it.
      required:
        - importId
        - status
        - progress
        - counts
        - skippedColumns
        - rows
        - rowsTruncated
        - createdAt
      properties:
        importId:
          type: string
          description: The import.
          example: 01JD8Y7Q3N4M5P6R7S8T9V0W1X
        status:
          type: string
          enum:
            - Previewing
            - Ready
            - Applying
            - Completed
            - Failed
            - CompletedWithFailures
          description: Where it stands.
          example: Ready
        progress:
          $ref: '#/components/schemas/EmployeeImportProgressDto'
          description: 'How far applying has got: rows tried of rows to save.'
        counts:
          $ref: '#/components/schemas/EmployeeImportCountsDto'
          description: >-
            How many rows the file holds, change, match the employee already,
            cannot be applied and, after applying, were updated or failed.
        skippedColumns:
          type: array
          items:
            $ref: '#/components/schemas/EmployeeImportSkippedColumnDto'
          description: Columns that change nothing, and why.
        rows:
          type: array
          items:
            $ref: '#/components/schemas/EmployeeImportRowDto'
          description: >-
            The rows that change something or cannot be applied, in file order:
            at most 1,000, `rowsTruncated` says when there are more.
        rowsTruncated:
          type: boolean
          description: More rows than `rows` holds.
          example: false
        error:
          type: string
          nullable: true
          description: Why it failed.
          example: 'Add the Employee ID column: it is how each row finds its employee.'
        createdAt:
          type: string
          format: date-time
          description: When the file was sent.
          example: '2026-11-08T10:00:00Z'
        completedAt:
          type: string
          format: date-time
          nullable: true
          description: When applying finished.
          example: '2026-11-08T10:00:40Z'
    EmployeeImportColumnDto:
      type: object
      description: Which employee field a column of the file holds.
      required:
        - header
        - field
      properties:
        header:
          type: string
          description: The column's heading, exactly as in the file.
          example: Team
        field:
          type: string
          description: An employee field as `GET /employee-fields` lists it.
          example: department
    MessageWithTokenResponse:
      type: object
      required:
        - text
        - token
      properties:
        text:
          type: string
          description: The message in English, ready to show to a user.
        token:
          type: string
          description: >-
            A stable snake_case key for the message, so a client can show its
            own wording or a translation instead of text.
        tokenArguments:
          type: array
          items:
            $ref: '#/components/schemas/ValidationIssueArgument'
          nullable: true
          description: >-
            Values to fill into the client's own wording for token, such as the
            request id; absent when the message has none.
    ValidationIssue:
      type: object
      description: >-
        Field validation item. `reasonToken` is a stable snake_case key for i18n
        or custom client messages; `reason` is the default English text from the
        API.
      required:
        - field
        - reason
        - reasonToken
      properties:
        field:
          type: string
          description: Dot-path of the field (e.g. name.firstName, address.line1).
        reason:
          type: string
          description: Default human-readable message.
        reasonToken:
          type: string
          description: Machine-readable error identifier (snake_case).
        reasonTokenArguments:
          type: array
          items:
            $ref: '#/components/schemas/ValidationIssueArgument'
          nullable: true
          description: >-
            Values to fill into the client's own wording for reasonToken; absent
            when the reason has none.
    EmployeeImportProgressDto:
      type: object
      description: How far applying has got.
      required:
        - done
        - total
      properties:
        done:
          type: integer
          format: int32
          description: 'Rows tried so far: updated or failed.'
          example: 3120
        total:
          type: integer
          format: int32
          description: Rows to save.
          example: 7000
    EmployeeImportCountsDto:
      type: object
      description: The rows of an import, counted.
      required:
        - total
        - changed
        - unchanged
        - problems
        - updated
        - failed
      properties:
        total:
          type: integer
          format: int32
          description: Employee rows in the file.
          example: 120
        changed:
          type: integer
          format: int32
          description: Rows that change at least one field.
          example: 37
        unchanged:
          type: integer
          format: int32
          description: Rows equal to the employee now; nothing is sent for them.
          example: 80
        problems:
          type: integer
          format: int32
          description: Rows that cannot be applied as they are.
          example: 3
        updated:
          type: integer
          format: int32
          description: 'After applying: employees updated.'
          example: 36
        failed:
          type: integer
          format: int32
          description: 'After applying: rows that could not be saved; each says why.'
          example: 1
    EmployeeImportSkippedColumnDto:
      type: object
      description: A column of the file that changes nothing, and why.
      required:
        - header
        - reason
      properties:
        header:
          type: string
          description: The column's heading.
          example: Payroll ID
        reason:
          type: string
          description: Why it is left out.
          example: Cannot be changed from a file; it has its own screen
    EmployeeImportRowDto:
      type: object
      description: A row of the file that changes something, or cannot be applied.
      required:
        - row
        - employeeId
        - changes
        - problems
      properties:
        row:
          type: integer
          format: int32
          description: The row's line in the file, counting the headings as 1.
          example: 2
        employeeId:
          type: string
          description: The employee the row names.
          example: 01JD8Y7Q3N4M5P6R7S8T9V0W1X
        changes:
          type: array
          items:
            $ref: '#/components/schemas/EmployeeImportChangeDto'
          description: The fields it changes.
        problems:
          type: array
          items:
            $ref: '#/components/schemas/EmployeeImportProblemDto'
          description: Why it cannot be applied, or why some of its cells are left out.
        outcome:
          type: string
          nullable: true
          description: >-
            After applying: `Updated`, or `Failed` with the reason in
            `problems`.
          example: Updated
    ValidationIssueArgument:
      type: object
      required:
        - name
        - value
      properties:
        name:
          type: string
          description: The name of the value, as a client's wording refers to it.
        value:
          description: The value itself, as a string, number or other JSON value.
    EmployeeImportChangeDto:
      type: object
      description: One field a row changes.
      required:
        - field
      properties:
        field:
          type: string
          description: >-
            The column that changes it: its "Existing employees" import key when
            the file's heading is one of that import's (`firstName`, `taxCode`),
            else the field's path.
          example: department
        from:
          type: string
          nullable: true
          description: Its value now; absent when it has none.
          example: Sales
        to:
          type: string
          nullable: true
          description: Its value after the update.
          example: Operations
    EmployeeImportProblemDto:
      type: object
      description: A cell, or a whole row, that cannot be applied, and what to do about it.
      required:
        - message
      properties:
        column:
          type: string
          nullable: true
          description: The column, when one cell is the problem.
          example: Hours per week
        message:
          type: string
          description: What is wrong, in words to act on.
          example: Enter a number, such as 1234.56, not "lots"
  securitySchemes:
    XAuthToken:
      type: apiKey
      in: header
      name: X-Auth-Token
      description: Access token obtained from OAuth2 client credentials flow
    XOrgId:
      type: apiKey
      in: header
      name: X-Org-Id
      description: >-
        Organisation to scope the request to. Required when the principal can
        access more than one organisation; optional for single-organisation
        principals (the authorizer resolves it automatically).

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.