Bulk Operations
Several endpoints support bulk operations alongside their single-record counterparts. The request shape differs depending on what the operation does — see the three patterns below.
This section tracks which resources have a documented bulk endpoint. Add a row here whenever a new one is confirmed.
1. Bulk create (POST)
A POST to {resource}_bulk takes a JSON array of the same object type its single-create counterpart takes, and creates each item independently. If one item fails, the other items in the same request are still created. The exception is a missing required field: if any item is missing one, the whole request fails and no items are created. See Bulk Create Employees for a fully worked example, including the request and response shapes.
| Resource | Single-create endpoint | Bulk endpoint |
|---|---|---|
| Employees | POST /wep/companies/{CLIENT_ID}/employees | POST /wep/companies/{CLIENT_ID}/employees_bulk |
| Divisions | POST /wep/companies/{companyId}/divisions | POST /wep/companies/{companyId}/divisions_bulk |
| Earning Code | POST /wep/companies/{companyId}/earningcodes | POST /wep/companies/{companyId}/earningcodes_bulk |
| Cost Code | POST /wep/companies/{companyId}/jobcosting/{jobCostingId}/codes | POST /wep/companies/{companyId}/jobcosting/{jobCostingId}/codes_bulk |
| Work Location | POST /wep/companies/{companyId}/worklocations | POST /wep/companies/{companyId}/worklocations_bulk |
Notes
- The response shape (each array entry as
{ data: <object> }on success or{ errorCode, errorMessage }on failure, matched to the request by array index).
Creates multiple employee records under a company in a single request. Use this instead of Create an Employee when you're importing several employees at once.
Endpoint
POST https://api.worklio.com/wep/companies/{CLIENT_ID}/employees_bulk
Requires a bearer access token (see How to Get API Access). CLIENT_ID is the company identifier returned as id when you create the company.
Request body
The request body is a single JSON array of employee objects. Each object takes the same fields as Create an Employee:
| Field | Type | Required | Description |
|---|---|---|---|
firstName | string | Required | Employee's first name |
lastName | string | Required | Employee's last name |
ssn | string | Not required by employee_bulk but will fail with SSN missing | Social Security Number, with or without dashes |
birthDate | string (ISO date) | Required | Date of birth |
employeeType | integer | Required | 0 = Employee, 1 = Contractor |
Example request
require("dotenv").config();
const TOKEN = process.env.API_KEY;
const CLIENT_ID = process.env.CLIENT_ID;
const url = `https://api.worklio.com/wep/companies/${CLIENT_ID}/employees_bulk`;
const payload = [
{
firstName: 'Michael',
lastName: 'Brown',
employeeType: 0,
ssn: '111-11-4084',
birthDate: '1990-07-14T00:00:00.000Z',
},
{
firstName: 'Sarah',
lastName: 'Nguyen',
employeeType: 0,
ssn: '122-22-2281',
birthDate: '1988-02-23T00:00:00.000Z',
}
];
async function createEmployeesBulk() {
const response = await fetch(url, {
method: "POST",
headers: {
accept: "application/json",
"api-version": "2.0",
authorization: `Bearer ${TOKEN}`,
"content-type": "application/json",
"x-api-version": "2.0"
},
body: JSON.stringify(payload)
});
console.log(response.status);
console.log(await response.text());
}
createEmployeesBulk();Example response
The response is an array in the same order as the request. Each entry is either { data: <employee> } on success, or { errorCode, errorMessage } on failure for that employee.
[
{
"errorCode": "UnknownError",
"errorMessage": "Employee with the same SSN/FEIN already has an active employment with the current client."
},
{
"data": {
"id": 4301,
"firstName": "Sarah",
"lastName": "Nguyen",
"employeeType": 0,
"ssnMasked": "***-**-2281",
"ssn": "122-22-2281",
"birthDate": "1988-02-23",
"payAllocation": [
{
"id": 0,
"order": 1,
"payMethod": 1,
"allocatedBy": 2,
"amount": 100
}
],
"isAdmin": false,
"employeeUI_ID": 7,
"country": "US",
"citizenshipCountry": "US",
"citizenship": 1,
"w2Info": {
"electronicOnly": false,
"firstName": "Sarah",
"lastName": "Nguyen"
},
"hrKeyDates": {
"liabilityStart": "2026-09-08",
"originalHire": "2026-09-08"
}
}
}
]In this example, Michael Brown's entry failed because his ssn already belongs to an active employee for this company, while Sarah Nguyen's employee record was created successfully. Check each entry's shape (errorCode/errorMessage vs. data) to tell which employees in the batch succeeded.
Notes
- This is not all-or-nothing: employees are created independently, so a failure on one doesn't prevent the others in the same request from being created.
- Match response entries to request entries by array position (index) to know which employee an error applies to.
Bulk create has two kinds of failureA missing required field fails the whole request. For example, if one employee has no name, no employees are created (example response below).
A missing
ssnor a duplicatessnfails only that employee. The response contains anerrorCodeanderrorMessagefor that employee, and the other employees are created (example response above).
ssn is required to create an employee, but employees_bulk checks it per employee, not before the request is accepted. This differs from Create an Employee, where ssn is required up front.
{
status: 3,
code: 'DataValidationError',
errorCode: '',
message: 'One or more validation errors occurred.\r\n' +
'[0].FirstName: [requiredError:The field is required.]',
stackTrace: '',
pagination: {
pageNo: 0,
pageSize: 0,
totalRecords: 0,
totalPages: 0,
dataToken: ''
},
validationErrors: { '[0].firstName': [ [Array] ] }
}2. Bulk update (PATCH)
A PATCH to the same {resource}_bulk URL used for bulk create takes a JSON object instead of an array. Each key is the id of an existing record, and each value is a partial object containing only the fields to change — fields you omit are left unchanged. Records in the batch are processed independently, same as bulk create. See Bulk Update Work Locations for a fully worked example.
| Resource | Bulk update endpoint |
|---|---|
| Divisions | PATCH /wep/companies/{companyId}/divisions_bulk |
| Cost Code | PATCH /wep/companies/{companyId}/jobcosting/{jobCostingId}/codes_bulk |
| Work Location | PATCH /wep/companies/{companyId}/worklocations_bulk |
Example request
require("dotenv").config();
const TOKEN = process.env.API_KEY;
const CLIENT_ID = process.env.CLIENT_ID;
const URL = process.env.URL
const url = `${URL}/wep/companies/${CLIENT_ID}/divisions_bulk`;
const payload = {
"138":{
name: "Support Divisions"
},
"1119":{ //the id doesnt exist
name: "This will result in an error"
}
};
async function bulkPatchDivisions() {
console.log(url)
const response = await fetch(url, {
method: "PATCH",
headers: {
accept: "application/json",
"api-version": "2.0",
authorization: `Bearer ${TOKEN}`,
"content-type": "application/json",
"x-api-version": "2.0"
},
body: JSON.stringify(payload)
});
console.log(response.status);
const raw = await response.text();
console.log(JSON.stringify(JSON.parse(raw), null, 2));
}
bulkPatchDivisions();Example response
The response is an array in the same order as the request. Each entry is either { data: <employee> } on success, or { errorCode, errorMessage } on failure for that id.
[
{
"data": {
"divisionAddress": {
"id": 6142,
"addressLine1": "233 S Wacker Drive",
"addressLine2": "Suite 8400",
"addressCity": "Chicago",
"addressState": "IL",
"addressZIP": "60606",
"addressCountry": "US"
},
"id": 138,
"name": "Support Divisions",
"tradeName": "",
"status": 1
}
},
{
"errorCode": "UnknownError",
"errorMessage": "Division #1119 does not exist."
}
]3. Bulk assign by ID list
POST https://api.worklio.com/wep/companies/{CLIENT_ID}/policies/{POLICY_ID}/employeesExample request
require("dotenv").config();
const TOKEN = process.env.API_KEY;
const CLIENT_ID = process.env.CLIENT_ID;
const POLICY_ID = process.env.POLICY_ID;
const url = `https://api.worklio.com/wep/companies/${CLIENT_ID}/policies/${POLICY_ID}/employees`;
const payload = {
employeeIds: [4277, 4278]
};
async function assignEmployeesToPolicy() {
const response = await fetch(url, {
method: "POST",
headers: {
accept: "application/json",
"api-version": "2.0",
authorization: `Bearer ${TOKEN}`,
"content-type": "application/json",
"x-api-version": "2.0"
},
body: JSON.stringify(payload)
});
console.log(response.status);
}
assignEmployeesToPolicy();Example response
204 No Content — no response body on success.
Notes
- Unlike §1 and §2, this endpoint gives no per-item success/failure detail — a 204 tells you the call as a whole succeeded, but not whether every ID in
employeeIdswas valid or actually got assigned. If you need to confirm an individual assignment, look it up separately after the call. - Response and error behavior for an invalid or already-assigned employee ID hasn't been captured yet.
Updated 4 days ago
