# Business Made · People

Part of AppEngine API Documentation. Full index: https://appengine.appmint.io/documentation.md
## GET /business-made/employees/overview

**HR overview**

`operationId: EmployeeController_overview`

The HR home figures in one read: people counts (headcount of active people, on leave, left in the last 12 months, unplaced), the five most recent hires, and today's time off when the Leave module is ready (who is off, decisions waiting, paid time owed). A location narrows it; in a one-location business people not yet placed anywhere count there.

#### Signature

```http
GET /business-made/employees/overview (businessLocationId?: string) -> `{ location, people:{headcount, onLeave, leftLast12Months, unplaced, locations}, recentHires:[{sk, name, initials, jobTitle, department, status, …}], timeOff:{state, …} }`
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `businessLocationId` | query | string | — | Location; `all` = the whole business. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | `{ location, people:{headcount, onLeave, leftLast12Months, unplaced, locations}, recentHires:[{sk, name, initials, jobTitle, department, status, …}], timeOff:{state, …} }` |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /business-made/employees

**List employees**

`operationId: EmployeeController_getEmployees`

Lists employees with arbitrary query filtering. The dedicated filter routes below cover the common cases more legibly.

#### Signature

```http
GET /business-made/employees (department?: string, status?: string, page?: integer, pageSize?: integer) -> Employees
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Employee records contain personal data — restrict who can reach this.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /business-made/employees/{id}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `department` | query | string | — |  |
| `status` | query | string | — |  |
| `page` | query | integer | — |  |
| `pageSize` | query | integer | — |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Employees |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /business-made/employees

**Create an employee**

`operationId: EmployeeController_createEmployee`

Creates an employee record. The business `employeeId` must be unique within the org — a collision is refused with a `409` naming the id, rather than creating a second record for the same person.

#### Signature

```http
POST /business-made/employees (body) -> The created employee
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `409` | EMPLOYEE_ID_EXISTS | Employee ID already exists | Another employee already uses that `employeeId`. | The body echoes the `employeeId`. Look up the existing record rather than creating a duplicate. |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /business-made/onboarding/{employeeId}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Request body

The employee to create.

```json
{
  "employeeId": "E-00412",
  "firstName": "Ada",
  "lastName": "Lovelace",
  "email": "ada@acme.com",
  "department": "engineering",
  "startDate": "2026-09-01"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The created employee |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `409` | Employee ID already exists — Another employee already uses that `employeeId`. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /business-made/employees/{id}

**Get an employee**

`operationId: EmployeeController_getEmployee`

Fetches one employee record.

#### Signature

```http
GET /business-made/employees/{id} (id: string) -> The employee
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `400` | INVALID_ID | invalid id <id> | The id is not a valid record id. A well-formed id that matches nothing returns an empty 200, not a 404. | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /business-made/employees/update`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `id` | path | string | yes | Employee id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The employee |
| `400` | invalid id <id> — The id is not a valid record id. A well-formed id that matches nothing returns an empty 200, not a 404. |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## DELETE /business-made/employees/{id}

**Delete an employee**

`operationId: EmployeeController_deleteEmployee`

Deletes an employee record outright.

For someone who has left, **terminate them instead** — that preserves the employment history, which is usually a legal retention requirement. Deletion is for a record created in error.

#### Signature

```http
DELETE /business-made/employees/{id} (id: string) -> Deletion result
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Destroys employment history. Termination is almost always the correct action.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /business-made/employees/{id}/terminate`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `id` | path | string | yes | Employee id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Deletion result |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /business-made/employees/update

**Update an employee**

`operationId: EmployeeController_updateEmployee`

Updates an employee record. Status changes and termination have dedicated endpoints that record the transition — use those rather than writing `status` here.

#### Signature

```http
POST /business-made/employees/update (body) -> The updated employee
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `400` | SK_REQUIRED | The employee id (sk) is required | Neither `sk` nor `id` is in the body. | — |
| `404` | EMPLOYEE_NOT_FOUND | Employee <sk> not found | No employee has that sk. | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /business-made/employees/{id}/status`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Request body

The employee to update, including its id.

```json
{
  "id": "EMP-4821",
  "department": "platform",
  "location": "london"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated employee |
| `400` | The employee id (sk) is required — Neither `sk` nor `id` is in the body. |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Employee <sk> not found — No employee has that sk. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /business-made/employees/department/{department}

**List employees by department**

`operationId: EmployeeController_getEmployeesByDepartment`

Employees in one department.

#### Signature

```http
GET /business-made/employees/department/{department} (department: string) -> Employees
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /business-made/employees/location/{location}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `department` | path | string | yes | Department code or id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Employees |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /business-made/employees/location/{location}

**List employees by location**

`operationId: EmployeeController_getEmployeesByLocation`

Employees at one location.

#### Signature

```http
GET /business-made/employees/location/{location} (location: string) -> Employees
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /business-made/employees/status/active`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `location` | path | string | yes | Location code. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Employees |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /business-made/employees/status/active

**List active employees**

`operationId: EmployeeController_getActiveEmployees`

Currently employed staff — the headcount denominator, excluding terminated and on-leave records.

#### Signature

```http
GET /business-made/employees/status/active () -> Active employees
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /business-made/employees/reports/headcount`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Active employees |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /business-made/employees/supervisor/{supervisorId}

**List an employee's direct reports**

`operationId: EmployeeController_getEmployeesBySupervisor`

Everyone reporting to one supervisor — direct reports only, not the whole tree beneath them.

#### Signature

```http
GET /business-made/employees/supervisor/{supervisorId} (supervisorId: string) -> Direct reports
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- One level deep. Walk it recursively for a full org tree, or use the org chart endpoints.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /business-made/organization/charts`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `supervisorId` | path | string | yes | Supervisor employee id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Direct reports |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /business-made/employees/skill/{skillName}

**Find employees by skill**

`operationId: EmployeeController_getEmployeesBySkill`

Everyone recorded as holding a skill — the staffing lookup when a project needs a particular competency.

#### Signature

```http
GET /business-made/employees/skill/{skillName} (skillName: string) -> Employees with the skill
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /business-made/employees/{id}/skills`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `skillName` | path | string | yes | Skill name. |
| `minProficiency` | query | string | yes |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Employees with the skill |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /business-made/employees/{id}/status

**Change an employee status**

`operationId: EmployeeController_updateEmployeeStatus`

Sets an employee's status with a reason — moving someone to leave, or back to active. Termination is separate because it does more than set a field.

#### Signature

```http
POST /business-made/employees/{id}/status (id: string, body) -> The updated employee
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | EMPLOYEE_NOT_FOUND | Employee not found | No employee has that id. | Check the id with `GET /business-made/employees`. The body carries `code` and the `employeeId` it tried. |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /business-made/employees/{id}/terminate`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `id` | path | string | yes | Employee id. |

### Request body

The new status.

```json
{
  "status": "on-leave",
  "reason": "Parental leave"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated employee |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Employee not found — No employee has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /business-made/employees/{id}/terminate

**Terminate an employee**

`operationId: EmployeeController_terminateEmployee`

Records the end of employment. Unlike a plain status change this is the formal termination — it is what the offboarding process, final pay and access revocation hang off.

The record is kept, which is the point: employment history has to survive the person leaving.

#### Signature

```http
POST /business-made/employees/{id}/terminate (id: string, body) -> The terminated employee
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Terminating does not revoke system access or issue final pay — those are offboarding steps.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | EMPLOYEE_NOT_FOUND | Employee not found | No employee has that id. | Check the id with `GET /business-made/employees`. The body carries `code` and the `employeeId` it tried. |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /business-made/offboarding`
- `POST /business-made/offboarding/{id}/revoke-access`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `id` | path | string | yes | Employee id. |

### Request body

Termination details.

```json
{
  "terminationDate": "2026-09-30",
  "reason": "Resignation",
  "rehireEligible": true
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The terminated employee |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Employee not found — No employee has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /business-made/employees/{id}/time-off/request

**Request time off**

`operationId: EmployeeController_requestTimeOff`

Raises a time-off request for an employee. It waits for approval — nothing is deducted from a balance until approved.

#### Signature

```http
POST /business-made/employees/{id}/time-off/request (id: string, body) -> The created request
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | EMPLOYEE_NOT_FOUND | Employee not found | No employee has that id. | Check the id with `GET /business-made/employees`. The body carries `code` and the `employeeId` it tried. |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /business-made/employees/{id}/time-off/{requestId}/approve`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `id` | path | string | yes | Employee id. |

### Request body

The request.

```json
{
  "type": "annual",
  "startDate": "2026-10-05",
  "endDate": "2026-10-09",
  "note": "Half-term"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The created request |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Employee not found — No employee has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /business-made/employees/{id}/time-off/{requestId}/approve

**Approve a time-off request**

`operationId: EmployeeController_approveTimeOff`

Approves a pending request. This is what commits the absence and draws down the balance.

#### Signature

```http
POST /business-made/employees/{id}/time-off/{requestId}/approve (id: string, requestId: string, body) -> The approved request
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | EMPLOYEE_NOT_FOUND | Employee not found | No employee has that id. | Check the id with `GET /business-made/employees`. The body carries `code` and the `employeeId` it tried. |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /business-made/employees/{id}/time-off/{requestId}/reject`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `id` | path | string | yes | Employee id. |
| `requestId` | path | string | yes | Time-off request id. |

### Request body

Optional approval note.

```json
{
  "note": "Approved — cover arranged"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The approved request |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Employee not found — No employee has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /business-made/employees/{id}/time-off/{requestId}/reject

**Reject a time-off request**

`operationId: EmployeeController_rejectTimeOff`

Declines a pending request, with a reason the employee will see.

#### Signature

```http
POST /business-made/employees/{id}/time-off/{requestId}/reject (id: string, requestId: string, body) -> The rejected request
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | EMPLOYEE_NOT_FOUND | Employee not found | No employee has that id. | Check the id with `GET /business-made/employees`. The body carries `code` and the `employeeId` it tried. |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /business-made/employees/{id}/time-off`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `id` | path | string | yes | Employee id. |
| `requestId` | path | string | yes | Time-off request id. |

### Request body

Why it was rejected.

```json
{
  "reason": "Two others already off that week"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The rejected request |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Employee not found — No employee has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /business-made/employees/{id}/time-off

**Get an employee's time off**

`operationId: EmployeeController_getTimeOffRequests`

An employee's time-off requests and their outcomes.

#### Signature

```http
GET /business-made/employees/{id}/time-off (id: string) -> Time-off records
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /business-made/employees/{id}/time-off/request`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `id` | path | string | yes | Employee id. |
| `status` | query | string | yes |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Time-off records |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /business-made/employees/{id}/reviews

**Get an employee's reviews**

`operationId: EmployeeController_getPerformanceReviews`

An employee's performance review history.

#### Signature

```http
GET /business-made/employees/{id}/reviews (id: string) -> Reviews
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /business-made/employees/{id}/reviews`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `id` | path | string | yes | Employee id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Reviews |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /business-made/employees/{id}/reviews

**Add a performance review**

`operationId: EmployeeController_addPerformanceReview`

Records a performance review against an employee.

#### Signature

```http
POST /business-made/employees/{id}/reviews (id: string, body) -> The created review
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Reviews are sensitive personal data — restrict read access as tightly as write access.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | EMPLOYEE_NOT_FOUND | Employee not found | No employee has that id. | Check the id with `GET /business-made/employees`. The body carries `code` and the `employeeId` it tried. |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /business-made/employees/{id}/reviews`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `id` | path | string | yes | Employee id. |

### Request body

The review.

```json
{
  "period": "2026-H1",
  "rating": 4,
  "summary": "Strong delivery on the platform migration"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The created review |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Employee not found — No employee has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /business-made/employees/{id}/training

**Assign training**

`operationId: EmployeeController_addTraining`

Assigns a training course to an employee. Where the training carries an expiry, it feeds the expired-training report.

#### Signature

```http
POST /business-made/employees/{id}/training (id: string, body) -> The assigned training
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | EMPLOYEE_NOT_FOUND | Employee not found | No employee has that id. | Check the id with `GET /business-made/employees`. The body carries `code` and the `employeeId` it tried. |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /business-made/employees/{id}/training/{trainingId}/complete`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `id` | path | string | yes | Employee id. |

### Request body

The training to assign.

```json
{
  "name": "Fire safety",
  "dueDate": "2026-10-31",
  "expiresAfterMonths": 12
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The assigned training |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Employee not found — No employee has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /business-made/employees/{id}/training/{trainingId}/complete

**Complete training**

`operationId: EmployeeController_completeTraining`

Marks assigned training as completed, which starts its expiry clock where one applies.

#### Signature

```http
POST /business-made/employees/{id}/training/{trainingId}/complete (id: string, trainingId: string, body) -> The completed training
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | EMPLOYEE_NOT_FOUND | Employee not found | No employee has that id. | Check the id with `GET /business-made/employees`. The body carries `code` and the `employeeId` it tried. |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /business-made/employees/training/expired`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `id` | path | string | yes | Employee id. |
| `trainingId` | path | string | yes | Training record id. |

### Request body

Completion details.

```json
{
  "completedDate": "2026-09-15",
  "score": 92
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The completed training |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Employee not found — No employee has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /business-made/employees/training/expired

**List expired training**

`operationId: EmployeeController_getExpiredTraining`

Training that has lapsed across the workforce — the compliance worklist.

For safety or regulatory training this is the report that matters: an employee with lapsed certification may not lawfully be doing the work.

#### Signature

```http
GET /business-made/employees/training/expired () -> Expired training records
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /business-made/employees/{id}/training`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Expired training records |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /business-made/employees/{id}/skills

**Add a skill**

`operationId: EmployeeController_addSkill`

Records a skill against an employee, making them findable through the skill lookup.

#### Signature

```http
POST /business-made/employees/{id}/skills (id: string, body) -> The updated employee
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | EMPLOYEE_NOT_FOUND | Employee not found | No employee has that id. | Check the id with `GET /business-made/employees`. The body carries `code` and the `employeeId` it tried. |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /business-made/employees/skill/{skillName}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `id` | path | string | yes | Employee id. |

### Request body

The skill to add.

```json
{
  "name": "kubernetes",
  "level": "advanced"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated employee |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Employee not found — No employee has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /business-made/employees/{id}/onboarding/start

**Start onboarding for an employee**

`operationId: EmployeeController_startOnboarding`

Starts onboarding with an explicit task list.

The dedicated onboarding controller (`/business-made/onboarding`) drives the same process from a configured template instead. Use this when the tasks are being supplied directly.

#### Signature

```http
POST /business-made/employees/{id}/onboarding/start (id: string, body) -> The onboarding record
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | EMPLOYEE_NOT_FOUND | Employee not found | No employee has that id. | Check the id with `GET /business-made/employees`. The body carries `code` and the `employeeId` it tried. |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /business-made/onboarding/{employeeId}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `id` | path | string | yes | Employee id. |

### Request body

The onboarding tasks.

```json
{
  "tasks": [
    {
      "key": "contract",
      "title": "Sign contract"
    },
    {
      "key": "laptop",
      "title": "Issue laptop"
    }
  ]
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The onboarding record |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Employee not found — No employee has that id. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /business-made/employees/{id}/onboarding/{taskId}/complete

**Complete an onboarding task**

`operationId: EmployeeController_completeOnboardingTask`

Marks one onboarding task complete for an employee.

#### Signature

```http
POST /business-made/employees/{id}/onboarding/{taskId}/complete (id: string, taskId: string) -> The updated onboarding record
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | ONBOARDING_NOT_FOUND | Onboarding not found | No onboarding matches. | The error body carries `code: "ONBOARDING_NOT_FOUND"` and the identifier. |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /business-made/onboarding/{employeeId}/items/{itemKey}/complete`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `id` | path | string | yes | Employee id. |
| `taskId` | path | string | yes | Onboarding task id. |

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated onboarding record |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Onboarding not found — No onboarding matches. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /business-made/employees/reports/headcount

**Get the headcount report**

`operationId: EmployeeController_getHeadcount`

Headcount broken down by department, location and status — the establishment figures.

#### Signature

```http
GET /business-made/employees/reports/headcount () -> Headcount report
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /business-made/organization/metrics`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Headcount report |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /business-made/employees/reports/anniversaries

**Get work anniversaries**

`operationId: EmployeeController_getUpcomingAnniversaries`

Upcoming service anniversaries — what an internal comms or recognition programme reads.

#### Signature

```http
GET /business-made/employees/reports/anniversaries () -> Upcoming anniversaries
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /business-made/employees/reports/birthdays`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `days` | query | number | yes |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Upcoming anniversaries |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /business-made/employees/reports/birthdays

**Get upcoming birthdays**

`operationId: EmployeeController_getUpcomingBirthdays`

Upcoming employee birthdays. Dates of birth are personal data, and some people prefer them not shared — check consent before surfacing this internally.

#### Signature

```http
GET /business-made/employees/reports/birthdays () -> Upcoming birthdays
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Exposes personal data that not everyone wants published.

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /business-made/employees/reports/anniversaries`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `days` | query | number | yes |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Upcoming birthdays |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /business-made/onboarding

**List onboarding records**

`operationId: OnboardingController_list`

Onboarding in progress across the org — who is still being brought on, and how far along they are.

#### Signature

```http
GET /business-made/onboarding () -> Onboarding records
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /business-made/onboarding/{employeeId}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `status` | query | string | yes |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Onboarding records |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## GET /business-made/onboarding/{employeeId}

**Get an employee's onboarding**

`operationId: OnboardingController_getOne`

The onboarding checklist for one employee and its progress.

#### Signature

```http
GET /business-made/onboarding/{employeeId} (employeeId: string) -> The onboarding record
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | EMPLOYEE_NOT_FOUND | Employee not found | No employee has that id or code. | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /business-made/onboarding/{employeeId}/complete`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `employeeId` | path | string | yes | Employee id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The onboarding record |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Employee not found — No employee has that id or code. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /business-made/onboarding/{employeeId}

**Start onboarding**

`operationId: OnboardingController_start`

Starts the onboarding process for a new hire, generating the checklist from the configured template.

This is the HR process run **during hiring** — collecting documents, issuing equipment, granting access. It is not the staff portal, which serves people who are already onboarded.

#### Signature

```http
POST /business-made/onboarding/{employeeId} (employeeId: string, body) -> The onboarding record
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | EMPLOYEE_NOT_FOUND | Employee not found | No employee has that id or code. | — |
| `409` | ONBOARDING_IN_PROGRESS | Onboarding already in progress | The person already has an open onboarding. | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /business-made/onboarding/{employeeId}/items/{itemKey}/complete`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `employeeId` | path | string | yes | Employee id. |

### Request body

Optional overrides for the generated checklist.

```json
{
  "startDate": "2026-09-01"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The onboarding record |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Employee not found — No employee has that id or code. |
| `409` | Onboarding already in progress — The person already has an open onboarding. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /business-made/onboarding/{employeeId}/items/{itemKey}/complete

**Complete an onboarding item**

`operationId: OnboardingController_completeItem`

Marks one checklist item done.

#### Signature

```http
POST /business-made/onboarding/{employeeId}/items/{itemKey}/complete (employeeId: string, itemKey: string, body) -> The updated onboarding record
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | EMPLOYEE_NOT_FOUND | Employee not found | No employee has that id or code. | — |
| `400` | ONBOARDING_CLOSED | Onboarding is already complete | The onboarding is complete (or cancelled: "Onboarding is cancelled"). | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /business-made/onboarding/{employeeId}/items/{itemKey}/skip`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `employeeId` | path | string | yes | Employee id. |
| `itemKey` | path | string | yes | Checklist item key. |

### Request body

Optional completion note.

```json
{
  "note": "Signed copy filed"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated onboarding record |
| `400` | Onboarding is already complete — The onboarding is complete (or cancelled: "Onboarding is cancelled"). |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Employee not found — No employee has that id or code. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /business-made/onboarding/{employeeId}/items/{itemKey}/skip

**Skip an onboarding item**

`operationId: OnboardingController_skipItem`

Marks an item as not required for this hire — a step that does not apply to their role or location. Distinct from completing it, so the checklist stays honest.

#### Signature

```http
POST /business-made/onboarding/{employeeId}/items/{itemKey}/skip (employeeId: string, itemKey: string, body) -> The updated onboarding record
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Notes

- Record a reason — a skipped compliance step with no explanation is an audit problem.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | EMPLOYEE_NOT_FOUND | Employee not found | No employee has that id or code. | — |
| `400` | ONBOARDING_CLOSED | Onboarding is already complete | The onboarding is complete (or cancelled: "Onboarding is cancelled"). | — |
| `403` | ITEM_REQUIRED | Item 'i9' is required and cannot be skipped | The item is required on the template. | — |

Plus the standard platform errors: `401`, `429`, `500`.

#### See also

- `POST /business-made/onboarding/{employeeId}/items/{itemKey}/complete`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `employeeId` | path | string | yes | Employee id. |
| `itemKey` | path | string | yes | Checklist item key. |

### Request body

Why it was skipped.

```json
{
  "reason": "Remote employee — no parking needed"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated onboarding record |
| `400` | Onboarding is already complete — The onboarding is complete (or cancelled: "Onboarding is cancelled"). |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | Item 'i9' is required and cannot be skipped — The item is required on the template. |
| `404` | Employee not found — No employee has that id or code. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /business-made/onboarding/{employeeId}/complete

**Complete onboarding**

`operationId: OnboardingController_complete`

Closes the onboarding process. Outstanding items should be completed or skipped first — closing with items open leaves the checklist unresolved.

#### Signature

```http
POST /business-made/onboarding/{employeeId}/complete (employeeId: string) -> The completed onboarding record
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | EMPLOYEE_NOT_FOUND | Employee not found | No employee has that id or code. | — |
| `400` | ONBOARDING_CLOSED | Onboarding is already complete | The onboarding is complete (or cancelled: "Onboarding is cancelled"). | — |
| `412` | ITEMS_PENDING | Required items still pending | Required items are still open; the body lists them in `outstanding`. | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `GET /business-made/onboarding/{employeeId}`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `employeeId` | path | string | yes | Employee id. |

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The completed onboarding record |
| `400` | Onboarding is already complete — The onboarding is complete (or cancelled: "Onboarding is cancelled"). |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Employee not found — No employee has that id or code. |
| `412` | Required items still pending — Required items are still open; the body lists them in `outstanding`. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

## POST /business-made/onboarding/{employeeId}/cancel

**Cancel onboarding**

`operationId: OnboardingController_cancel`

Abandons onboarding — a hire who never started. The record is kept, so a withdrawn offer is visible rather than vanishing.

#### Signature

```http
POST /business-made/onboarding/{employeeId}/cancel (employeeId: string, body) -> The cancelled onboarding record
```

#### Access

Requires a bearer JWT (`Authorization: Bearer <token>`).

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | EMPLOYEE_NOT_FOUND | Employee not found | No employee has that id or code. | — |
| `400` | ONBOARDING_CLOSED | Onboarding is already complete | The onboarding is complete (or cancelled: "Onboarding is cancelled"). | — |

Plus the standard platform errors: `401`, `403`, `429`, `500`.

#### See also

- `POST /business-made/onboarding/{employeeId}/complete`

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `orgid` | header | string | yes | Organization (tenant) identifier. Every request is scoped to this org; data from other orgs is never visible. Issued with your API credentials. |
| `employeeId` | path | string | yes | Employee id. |

### Request body

Why it was cancelled.

```json
{
  "reason": "Candidate withdrew"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The cancelled onboarding record |
| `400` | Onboarding is already complete — The onboarding is complete (or cancelled: "Onboarding is cancelled"). |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Employee not found — No employee has that id or code. |
| `429` | Too Many Requests — More than 100,000 requests from one IP within 5 minutes (configurable per deployment). CORS preflights and requests from inside the platform cluster are not counted. The limiter answers before the error filter, so the body is `{ statusCode, error, message }` with no `path`, `method` or `timeStamp`; the `RateLimit-*` response headers say when the window resets. |
| `500` | An unexpected error occurred. Our team has been notified. — An unhandled server-side failure. |

