How do you create an employee via the API?
POST /v1/employees creates an employee. Required body fields: email, name, surname, gender, and active. Access requires
the employees.write scope.
Endpoint
| Parameter | Value | | -------------- | ---------------------------------------------------- | | Method | POST | | Path
| /v1/employees | | Base URL | https://smartway.pro/api | | Auth | Bearer token | | Required scope | employees.write |
Purpose
This endpoint is used to create a new employee within the company from the Bearer token.
The active value controls not only the business flag, but also the creation or absence of a linked user in Keycloak.
Prerequisites
- The client must send a valid Bearer token.
- The token must contain company context.
- The token must contain the employees.write scope.
- idCompany is not sent in the body or query string.
- hrEmail is not sent by the external client; the BFF takes it from the Bearer token.
- In the current implementation, Idempotency-Key is not used.
Request
Body parameters
| Field | Type | Required | Description | | ----------- | -------- | ------------ |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| | email | string | yes | Employee email. | | name | string | yes | Employee first name. This is the source of truth
for generating fullName during creation. | | surname | string | yes | Employee surname. This is the source of truth for
generating fullName during creation. | | gender | string | yes | Only Male or Female are allowed. | | active | boolean |
yes | Only JSON-boolean values true or false are allowed. true creates or synchronises personal account access through
Keycloak; false creates the employee without that access. | | department | string | no | Primary department. If not
provided, null is stored. | | departments | string[] | no | Set of departments. If not provided, the collection remains
empty. | | jobTitle | string | no | Primary job title. If not provided, null is stored. | | jobTitles | string[] | no |
Set of job titles. If not provided, the collection remains empty. | | phone | string | no | Phone number. If not
provided, null is stored. | | notes | string | no | Notes. If not provided, null is stored. |
curl example
Create an employee with primary fields and arrays
curl -X POST 'https://smartway.pro/api/v1/employees' \
-H 'Authorization: Bearer <access_token>' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
--data-binary @- <<'JSON'
{
"email": "employee@example.com",
"name": "Ivan",
"surname": "Petrenko",
"gender": "Female",
"active": false,
"department": "Management",
"departments": ["КЛ"],
"jobTitle": "Manager",
"jobTitles": ["Coordinator"],
"phone": "+380000000000",
"notes": "New employee from public API"
}
JSON
Create an employee only with departments/jobTitles arrays
curl -X POST 'https://smartway.pro/api/v1/employees' \
-H 'Authorization: Bearer <access_token>' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
--data-binary @- <<'JSON'
{
"email": "employee@example.com",
"name": "Ivan",
"surname": "Petrenko",
"gender": "Male",
"active": true,
"departments": ["Management", "КЛ"],
"jobTitles": ["Manager", "Coordinator"],
"phone": "+380000000000",
"notes": "New employee from public API"
}
JSON
Response
Successful response: 201 Created. Returns the created Employee.
{
"employeeId": 4432,
"candidateId": 10748,
"email": "employee@example.com",
"fullName": "Ivan Petrenko",
"name": "Ivan",
"surname": "Petrenko",
"gender": "Female",
"department": "Management",
"departments": [
"Management",
"КЛ"
],
"jobTitle": "Manager",
"jobTitles": [
"Coordinator",
"Manager"
],
"phone": "+380000000000",
"active": false
}
Response fields
Employee
| Field | Type | Description | | ----------- | -------- | ------------------------------------------------------ | |
employeeId | int64 | Employee ID. | | candidateId | int64 | Linked candidate ID. | | email | string | Employee email. |
| fullName | string | Full name generated by the server from name + surname. | | name | string | First name. | | surname
| string | Surname. | | gender | string | Gender. | | department | string | Primary department. | | departments |
string[] | Set of departments. | | jobTitle | string | Primary job title. | | jobTitles | string[] | Set of job titles.
| | phone | string | Phone number. | | active | boolean | Active flag. |
Business logic
- The BFF determines companyId only from the Bearer token.
- The BFF takes hrEmail from the Bearer token. hrEmail equals the email of the user with the HRADMIN role who created
or rotated the active company API key.
- If the active API key is rotated by another HRADMIN, subsequent create operations automatically start using that
HRADMIN's hrEmail.
- Repeating the same POST does not create a duplicate if an employee with the same email already exists; the API
returns 409 Conflict.
- gender accepts only Male or Female.
- active accepts only JSON-boolean true or false.
- active = true creates or synchronises the linked user account in Keycloak and enables access to the personal
account.
- active = false creates an employee without access to the personal account, that is, without a user in Keycloak.
- fullName in the response is generated by the server from name + surname.
- If only departments or jobTitles is provided, the first array element becomes the primary value in department or
jobTitle.
- If both a single field and an array are provided, the single field takes priority as primary, and the array is
supplemented with unique values.
- If department, jobTitle, phone, or notes is not provided, null is stored in the corresponding single field.
- If departments or jobTitles is not provided, the corresponding collection remains empty.
- In the response, departments and jobTitles are returned in a stable sorted order.
Edge cases
Edge cases
| Scenario | API behaviour | | ---------------------------------------- |
--------------------------------------------------------------------------------------------------- | | A required field
is missing | The API returns 400 Bad Request. | | gender is not Male or Female | The API returns 400 Bad Request with an
explanation. | | active is sent as a string or number | The API returns 400 Bad Request because active must be
JSON-boolean. | | email already exists | The API returns 409 Conflict. | | Only departments is provided | The first
array element becomes department. | | department and departments are provided | department remains primary, and
departments is supplemented with unique values. | | department or jobTitle is not provided | null is stored in the
corresponding single field. | | departments or jobTitles is not provided | The corresponding collection remains empty. |
| The client sends fullName | fullName is not described in the create request. The server generates fullName from name +
surname. |
Errors
Error responses
| HTTP status | When it occurs | | ------------------------- |
------------------------------------------------------------------------------------ | | 400 Bad Request | At least one
required parameter is missing: email, name, surname, gender, or active. | | 400 Bad Request | gender is not Male or
Female. | | 400 Bad Request | active is not a JSON-boolean value true or false. | | 401 Unauthorized | Bearer token is
missing or invalid. | | 403 Forbidden | Insufficient permissions or token without company context. | | 409 Conflict | An
Employee with this email already exists. | | 500 Internal Server Error | Unexpected BFF error. | | 503 Service
Unavailable | Internal integration failure BFF -> back2. |
Usage
- Create an active employee with access to the personal account: active = true.
- Create an employee without access to the personal account: active = false.
- Send multiple departments or job titles with a defined primary value.
- Synchronise employees from an external HR system.
Common mistakes
Typical integration mistakes
| Common mistake | Correct approach | | -------------------------------------------------- |
------------------------------------------------------------------- | | Sending active as the string "true" or "false" |
Send true or false as a JSON boolean. | | Sending gender with a value other than Male/Female | Use only Male or Female.
| | Sending idCompany or hrEmail | Do not send these values; the BFF takes them from the Bearer token. | | Expecting
idempotency through Idempotency-Key | In the current implementation, Idempotency-Key is not used. | | Sending fullName
instead of name and surname | Send name and surname; the server generates fullName. |
FAQ
Which fields are required to create an employee?
email, name, surname, gender, and active.
What does active = true do?
The server creates or synchronises the linked user account in Keycloak and enables access to the personal account.
What does active = false do?
The employee is created without access to the personal account, that is, without a user in Keycloak.
What happens if the email already exists?
The API returns 409 Conflict.
Do I need to send hrEmail?
No. The BFF takes hrEmail from the Bearer token.