Create new employees for a specified employer

Add MCP server to your AI tool

Allow AI tools and LLMs to interact with the API documentation portal through MCP.

MCP server URL

https://docs.getpenfold.dev/mcp

Standard setup for AI tools providing an mcp.json file

mcp.json
"Penfold Partner API MCP server": {
  "url": "https://docs.getpenfold.dev/mcp"
}
Close
POST /employers/{employer_id}/employees

Create new employees for a specified employer. Asynchronous — the response is an Upload object representing queued work, not a final result. Member identifiers are not available in this response; poll GET /uploads/{upload_id} until the status is terminal, then fetch enrolments via GET /uploads/{upload_id}/enrolments and any errors via GET /uploads/{upload_id}/errors. Processing is idempotent: re-submitting the same records will only process those not already enrolled successfully.

Path parameters

  • employer_id string Required
application/json

Body Required

Employee creation data.

  • email string(email) Required

    Email address of the employee.

  • forename string Required

    The employee's first name.

  • surname string Required

    The employee's last name.

  • title string Required

    The employee's title, e.g. Mr., Mrs., Dr., etc.

  • date_of_birth string(date) Required

    The employee's date of birth, in YYYY-MM-DD format.

  • address_line1 string Required

    The first line of the employee's address.

  • postcode string Required

    The postal code of the employee's address.

  • national_insurance_number string(national_insurance_number) Required

    The employee's National Insurance number.

  • employment_start_date string(date) Required

    The date the employee started their employment, in YYYY-MM-DD format.

Responses

  • 201 application/json

    Submission created successfully for the employees.

    Hide response attributes Show response attributes object
    • id string Required

      ID of the upload.

    • employer_id string | null

      ID of the employer the upload was made for.

    • put_destination_url string | null Required

      Destination URL where the actual file should be sent, using a PUT request. Only present when status is AwaitingFile. We recommend using the AWS S3 client SDK to perform the upload.

    • created_at string(date-time) Required

      Datetime the upload was created.

    • updated_at string(date-time) Required

      Datetime the upload was last updated.

    • processing_started string(date-time) | null Required

      Datetime of when processing of the file began.

    • processing_ended string(date-time) | null Required

      Datetime of when processing of the file ended, or null if it has not ended.

    • processing_time number | null Required

      The number of seconds it took to process the file end-to-end (if applicable).

    • total_errors number Required

      The number of errors produced in processing the file. Zero if there are none or processing is not finished. Use the {upload_id}/errors endpoint to retrieve detailed information of the errors.

    • contributions_created number Required

      The number of contributions created during processing. Zero if processing is not finished.

    • contributions_unprocessed number

      The number of contributions that failed to be created during processing. Zero if processing is not finished.

    • contributions_already_existed number Required

      The number of contributions that already existed prior to processing, and which have been skipped during processing of the upload. Zero if processing is not finished.

    • employer_contributions number Required

      The total of employer contributions created for the upload, excluding contributions that already existed. Zero if processing is not finished.

    • employee_contributions number Required

      The total of employee contributions created for the upload, excluding contributions that already existed. Zero if processing is not finished.

    • total_contributions number Required

      The total of contributions created for the upload, excluding contributions that already existed. Zero if processing is not finished.

    • filename string | null Required

      The filename of the uploaded file (if applicable).

    • status string Required

      The status of the upload. Error indicates the file was not able to be processed. PartiallyProcessed indicates that validation passed and processing ran eagerly, but some records succeeded while others produced errors — treat this status as incomplete and re-submit the failing records (or the entire file) to recover. Processing is idempotent per pay period, so successfully-processed records will be skipped on re-submission. To retrieve the errors for an Error or PartiallyProcessed upload, use the /uploads/{upload_id}/errors endpoint.

      Values are AwaitingFile, ReceivedFile, Processing, Processed, Error, Timeout, or PartiallyProcessed.

  • 400 application/json

    Bad request, the request is malformed or contains invalid data.

    Hide response attributes Show response attributes object
    • error string

      A descriptive error message.

    • validation_errors array[object]
      Hide validation_errors attributes Show validation_errors attributes object
      • field string Required

        The name of the field that failed validation.

      • message string Required

        A descriptive error message.

  • 401 application/json

    Unauthorized, the request requires authentication, and the provided credentials are either missing or incorrect.

    Hide response attribute Show response attribute object
    • error string

      A descriptive error message.

  • 404 application/json

    Employer not found, the specified employer_id does not match any existing employer.

    Hide response attribute Show response attribute object
    • error string

      A descriptive error message.

POST /employers/{employer_id}/employees
curl \
 --request POST 'https://payroll-api.getpenfold.dev/v4/employers/{employer_id}/employees' \
 --header "Authorization: Bearer $ACCESS_TOKEN" \
 --header "Content-Type: application/json" \
 --data '[{"email":"john.doe@example.com","forename":"John","surname":"Doe","title":"Mr.","date_of_birth":"1990-01-01","address_line1":"123 Main Street","postcode":"AB12 3CD","national_insurance_number":"AA123456C","employment_start_date":"2023-01-01"}]'
Request examples
[
  {
    "email": "john.doe@example.com",
    "forename": "John",
    "surname": "Doe",
    "title": "Mr.",
    "date_of_birth": "1990-01-01",
    "address_line1": "123 Main Street",
    "postcode": "AB12 3CD",
    "national_insurance_number": "AA123456C",
    "employment_start_date": "2023-01-01"
  }
]
Response examples (201)
{
  "id": "string",
  "employer_id": "string",
  "put_destination_url": "string",
  "created_at": "2026-05-04T09:42:00Z",
  "updated_at": "2026-05-04T09:42:00Z",
  "processing_started": "2023-03-01T11:00:00Z",
  "processing_ended": "2023-03-01T12:00:00Z",
  "processing_time": 47,
  "total_errors": 10,
  "contributions_created": 5,
  "contributions_unprocessed": 0,
  "contributions_already_existed": 0,
  "employer_contributions": "500.34",
  "employee_contributions": "734.11",
  "total_contributions": "1234.45",
  "filename": "papdis.csv",
  "status": "Processed"
}
Response examples (400)
{
  "error": "Bad request: invalid data provided.",
  "validation_errors": [
    {
      "field": "email",
      "message": "Email address is invalid."
    }
  ]
}
Response examples (401)
{
  "error": "Bad request: invalid data provided."
}
Response examples (404)
{
  "error": "Bad request: invalid data provided."
}