Employees
Employees represent the individuals reported under the Management Control pillar of a BEEtoolkit Scorecard. This API allows you to fetch and upsert employees for a specific scorecard.
Employees are always scoped to a single scorecard and are stored against that scorecard's ID.
Endpoints
| Method | Path | Purpose |
|---|---|---|
GET | /api/public/v1/scorecards/{scorecard_id}/employees | Retrieve all employees for a scorecard |
POST | /api/public/v1/scorecards/{scorecard_id}/employees | Upsert (create or update) employees in bulk |
DELETE | /api/public/v1/scorecards/{scorecard_id}/employees/{employee_id} | Delete an employee using the BEEtoolkit-assigned ID for that employee |
All requests must be authenticated using your API key and HMAC signature, consistent with the other public APIs.
Include the following headers on every request:
Authorization: HMAC <signature>X-Api-Key: <your account key>Accept: application/json
See Authentication.
Data Model
Employee Attributes
Unless otherwise stated, all fields are optional from the API's perspective. However, some are required in practice to create valid B-BBEE records (see notes below).
Core Identity & Classification
| Field | Type | Example | Notes |
|---|---|---|---|
full_name | string | "John Smith" | Employee full name. |
id_number | string | "8801010120001" | 13-digit SA ID. Required unless foreign = true. Used as a primary external identifier. |
foreign | boolean | false | Marks the individual as a foreign national. If true, id_number may be omitted, or replaced with their passport number. |
gender | string | "Male" | See Gender values below. |
race | string | "Indian" | See Race values below. |
designation | string | "Semi-skilled" | Occupational level; see Designation values below. Required when creating a new employee. |
disabled | boolean | true | Indicates whether the employee is a person with a disability. |
Employment & Structure
| Field | Type | Example | Notes |
|---|---|---|---|
hire_date | date (string) | "2024-03-15" | Employment start date (YYYY-MM-DD). |
termination_date | date | null | null | Employment end date, if applicable. |
latest_promotion_date | date | null | null | Most recent promotion date. |
employee_code | string | "333" | Your internal employee identifier. Used as a secondary external identifier. |
location | string | "HO" | Site/branch, e.g. "HO", "Plant 1". |
business_unit | string | "HR" | Department or business unit. |
province | string | "Gauteng" | Province used for reporting. |
age | integer | 24 | Age of the employee (typically at period end). |
voting_rights | number | string | 0 or "0.0" | Voting rights percentage (for Ownership/MC linkage where applicable). |
System Fields (Response Only)
| Field | Type | Example | Notes |
|---|---|---|---|
id | integer | 54981057 | Internal ID of the employee record. |
created_at | datetime | "2026-01-15T07:59:32.000Z" | Creation timestamp. |
updated_at | datetime | "2026-01-15T07:59:32.000Z" | Last update timestamp. |
All dates must be provided in ISO8601 format: YYYY-MM-DD.
null is accepted where a date is not applicable (e.g. termination_date, latest_promotion_date).
Enumerations
The API expects values that align with your B-BBEE configuration. The lists below show typical values; your environment may be configured with a subset or variant of these.
Gender values
Typical supported values:
MaleFemale
Race values
Typical supported values:
AfricanColouredIndianWhite
Designation (Occupational level) values
Typical supported values:
Executive DirectorNon-executive DirectorOther Executive ManagerSenior ManagerMiddle ManagerJunior ManagerSemi-skilledUnskilled
Fetch Employees (Index)
Retrieve all employees captured against a specific scorecard.
GET /api/public/v1/scorecards/{scorecard_id}/employees
- cURL
- JavaScript (axios)
curl -X GET "https://www.beetoolkit.co.za/api/public/v1/scorecards/{scorecard_id}/employees" -H "Accept: application/json" -H "X-Api-Key: <your api key>" -H "Authorization: HMAC <your signature>"
const url = `https://www.beetoolkit.co.za/api/public/v1/scorecards/${scorecardId}/employees`;
const path = new URL(url).pathname;
const signature = await generateSignature(path, "", apiSecret, 5);
const response = await axios.get(url, {
httpsAgent,
headers: {
Accept: "application/json",
Authorization: `HMAC ${signature}`,
"X-Api-Key": apiKey,
},
});
console.log(response.data);
Example Response
[
{
"id": 54981057,
"gender": "Male",
"designation": "Semi-skilled",
"disabled": true,
"foreign": false,
"race": "Indian",
"full_name": "John Smith",
"id_number": "8801010120001",
"hire_date": null,
"termination_date": null,
"latest_promotion_date": null,
"employee_code": "333",
"location": "HO",
"business_unit": "HR",
"province": "Gauteng",
"age": 24,
"voting_rights": "0.0",
"created_at": "2026-01-15T07:59:32.000Z",
"updated_at": "2026-01-15T07:59:32.000Z"
}
]
Upsert Employees (Bulk)
Upserts operate on multiple employees at once via a batch payload.
POST /api/public/v1/scorecards/{scorecard_id}/employees
The request body must be wrapped in an employees array:
{
"employees": [
{
"gender": "Male",
"designation": "Semi-skilled",
"disabled": true,
"foreign": false,
"race": "Indian",
"full_name": "John Smith",
"id_number": "8801010120001",
"hire_date": null,
"termination_date": null,
"latest_promotion_date": null,
"employee_code": "333",
"location": "HO",
"business_unit": "HR",
"province": "Gauteng",
"age": 24,
"voting_rights": 0
}
]
}
- cURL
- JavaScript (axios)
curl -X POST "https://www.beetoolkit.co.za/api/public/v1/scorecards/{scorecard_id}/employees" -H "Accept: application/json" -H "Content-Type: application/json" -H "X-Api-Key: <your api key>" -H "Authorization: HMAC <your signature>" -d '{
"employees": [
{
"gender": "Male",
"designation": "Semi-skilled",
"disabled": true,
"foreign": false,
"race": "Indian",
"full_name": "John Smith",
"id_number": "8801010120001",
"employee_code": "333",
"location": "HO",
"business_unit": "HR",
"province": "Gauteng",
"age": 24,
"voting_rights": 0
}
]
}'
const scorecardId = "scorecard_b8Jyl56MPDRH3jrVMeqZkpDw";
const url = `https://www.beetoolkit.co.za/api/public/v1/scorecards/${scorecardId}/employees`;
const payload = {
employees: [
{
gender: "Male",
designation: "Semi-skilled",
disabled: true,
foreign: false,
race: "Indian",
full_name: "John Smith",
id_number: "8801010120001",
hire_date: null,
termination_date: null,
latest_promotion_date: null,
employee_code: "333",
location: "HO",
business_unit: "HR",
province: "Gauteng",
age: 24,
voting_rights: 0
}
]
};
const path = new URL(url).pathname;
const signature = await generateSignature(path, payload, apiSecret, 5);
const response = await axios.post(url, payload, {
httpsAgent,
headers: {
Accept: "application/json",
Authorization: `HMAC ${signature}`,
"X-Api-Key": apiKey,
},
});
console.log(response.data);
Successful Response
On success, the API returns the upserted employees as an array, using the same shape as the index endpoint:
[
{
"id": 54981057,
"gender": "Male",
"designation": "Semi-skilled",
"disabled": true,
"foreign": false,
"race": "Indian",
"full_name": "John Smith",
"id_number": "8801010120001",
"hire_date": null,
"termination_date": null,
"latest_promotion_date": null,
"employee_code": "333",
"location": "HO",
"business_unit": "HR",
"province": "Gauteng",
"age": 24,
"voting_rights": "0.0",
"created_at": "2026-01-15T07:59:32.000Z",
"updated_at": "2026-01-15T07:59:32.000Z"
}
]
Matching & Upsert Rules
Each record in the employees array is processed independently.
1. External identifier requirement
Every employee record must include at least one external identifier:
id_number, oremployee_code
If neither is present for any record, the entire request fails with a 422 Unprocessable Entity and a list of offending records.
{
"error": {
"message": "Employee records must include at least one external identifier (id_number or employee_code).",
"invalid_records": [
{
"full_name": "John NoId",
"gender": "Male"
}
]
}
}
2. Matching precedence
When identifiers are present, the API applies the following precedence to find an existing record within the scorecard's Management Control structure:
- By
id_number(if provided)- The system first attempts to find an existing employee where
id_numbermatches.
- The system first attempts to find an existing employee where
- By
employee_code(if no match was found byid_numberandemployee_codeis provided)- If an employee with the same
employee_codeexists, that record is used.
- If an employee with the same
If no matching employee is found using the above rules, a new employee record is initialised.
3. Attribute assignment
For matched or newly initialised records, the following attributes are assigned from the payload:
id_number
gender
designation
disabled
foreign
race
full_name
hire_date
termination_date
latest_promotion_date
employee_code
location
business_unit
province
age
voting_rights
Any other fields in the incoming payload are ignored.
Validation Errors
If one or more employee records fail validation (e.g. invalid ID number format), the API responds with 422 Unprocessable Entity and includes details of each invalid record.
Example (invalid SA ID number):
{
"error": {
"message": "One or more employee records are invalid.",
"invalid_records": [
{
"errors": [
"Id number needs to be a valid 13-digit SA ID number or individual should be marked as foreign"
],
"id_number": "888888877789",
"employee_code": "333",
"full_name": "John Smith"
}
]
}
}