# Business Made · Leave

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

**Is the Leave module ready**

`operationId: LeaveController_readiness`

One answer for every client: `state` is `off` (not switched on), `incomplete` (on, but something required is missing) or `ready`. `missing` lists what blocks it (basics saved, a leave type, a default policy, who is HR, the approval flow), `warnings` what is worth fixing (people routing to HR, unmapped job titles, locations with no country, no holidays this year). Each item carries a label and the setup-page route.

#### Signature

```http
GET /business-made/leave/setup/readiness () -> `{ state, enabled, enabledAt?, enabledBy?, missing:[{key,label,route}], warnings:[{key,label,route}], counts:{types,policies,employees,hr,routeToHr,onDefault,unmatched}, flow, unit }`
```

#### Access

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

#### Errors

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

#### See also

- `GET /business-made/leave/setup`
- `POST /business-made/leave/setup/enable`

### 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` | `{ state, enabled, enabledAt?, enabledBy?, missing:[{key,label,route}], warnings:[{key,label,route}], counts:{types,policies,employees,hr,routeToHr,onDefault,unmatched}, flow, unit }` |
| `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. |

Example response:

```json
{
  "state": "incomplete",
  "enabled": true,
  "missing": [
    {
      "key": "policy",
      "label": "Mark one active policy as the org default",
      "route": "/hr/time-off/setup#policies"
    }
  ],
  "warnings": [],
  "counts": {
    "types": 3,
    "policies": 1,
    "employees": 24,
    "hr": 1
  },
  "flow": "supervisor-hr",
  "unit": "days"
}
```

## GET /business-made/leave/setup

**Everything the setup page shows**

`operationId: LeaveController_getSetup`

Settings, readiness, who is HR, a suggested approval flow, every leave type and policy (with how many people each policy covers), the job-level ladder with title counts, how every person resolves (level, policy, approver), locations with their country, this year's calendar days, and the departments and employment types in use. `businessLocationId` shows that location's settings on top of the org's.

#### Signature

```http
GET /business-made/leave/setup (businessLocationId?: string) -> `{ settings, readiness, hr, suggestion, types, policies, levels:{ladder,counts,titles}, resolution:[{sk,employeeId,name,level,policyId,policyTitle,viaDefault,approver,…}], locations, calendar, year, departments, employmentTypes }`
```

#### Access

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

#### Errors

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

#### See also

- `GET /business-made/leave/setup/settings`

### 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 sk or slug; `all` or empty = org level. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | `{ settings, readiness, hr, suggestion, types, policies, levels:{ladder,counts,titles}, resolution:[{sk,employeeId,name,level,policyId,policyTitle,viaDefault,approver,…}], locations, calendar, year, departments, employmentTypes }` |
| `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/leave/setup/enable

**Switch Leave on or off**

`operationId: LeaveController_enable`

Turns the module on (default) or off. Returns the readiness after the change.

#### Signature

```http
POST /business-made/leave/setup/enable (body) -> The module readiness
```

#### Access

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

#### Errors

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

#### See also

- `GET /business-made/leave/setup/readiness`

### 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

```json
{
  "enabled": true
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The module readiness |
| `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/leave/setup/starter

**Create the starter types and default policy**

`operationId: LeaveController_starter`

First-run help: creates the starter leave types (`types: true`) and/or a default policy (`policy: true`). **Never overwrites** — a code the org already has is left alone.

#### Signature

```http
POST /business-made/leave/setup/starter (body) -> `{ types: <created count>, policy: <created policy or null>, retired?, policyBackfilled?, readiness }`
```

#### 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. |

### Request body

```json
{
  "types": true,
  "policy": true
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | `{ types: <created count>, policy: <created policy or null>, retired?, policyBackfilled?, readiness }` |
| `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/leave/setup/settings

**Get leave settings**

`operationId: LeaveController_getSettings`

The org's leave settings with defaults filled in: `unit` (days/hours), `leaveYear`, `workingDays`, `hrUsers`, `approval` (`baseFlow`, `escalateAfterDays`, `dailyDigest`), `jobLevels`, `holidaySource`. With a location, its overrides of `workingDays`, `hrUsers` and `holidaySource` are applied and listed in `location.overrides`. `saved` is false until the basics are saved once.

#### Signature

```http
GET /business-made/leave/setup/settings (businessLocationId?: string) -> The merged settings
```

#### 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 sk or slug. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The merged settings |
| `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. |

Example response:

```json
{
  "unit": "days",
  "leaveYear": {
    "type": "calendar"
  },
  "workingDays": [
    "mon",
    "tue",
    "wed",
    "thu",
    "fri"
  ],
  "hrUsers": [],
  "approval": {
    "baseFlow": "supervisor-hr",
    "escalateAfterDays": 3,
    "dailyDigest": true
  },
  "jobLevels": [],
  "holidaySource": "suggest",
  "saved": true,
  "location": {
    "slug": null,
    "rollup": true,
    "count": 2,
    "overrides": []
  }
}
```

## POST /business-made/leave/setup/settings

**Save leave settings**

`operationId: LeaveController_saveSettings`

Merges the patch into the org settings — or, with a location, saves only the keys a location may override (`workingDays`, `hrUsers`, `holidaySource`). Afterwards every person's level and policy are re-stamped, and the approval escalation (`approval.escalateAfterDays`) is written onto the leave-approval workflow.

#### Signature

```http
POST /business-made/leave/setup/settings (businessLocationId?: string, body) -> The merged settings after the save
```

#### Access

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

#### Errors

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

#### See also

- `POST /business-made/leave/setup/recompute`

### 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 | — |  |

### Request body

```json
{
  "unit": "days",
  "approval": {
    "baseFlow": "supervisor-hr",
    "escalateAfterDays": 2
  }
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The merged settings after the save |
| `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/leave/setup/recompute

**Re-stamp levels and policies on everyone**

`operationId: LeaveController_recompute`

Works out each person's job level and leave policy from the settings and writes them to `employment.jobLevel` / `employment.leavePolicyId`, so balances and payroll know which policy applies. Runs on its own after a settings save.

#### Signature

```http
POST /business-made/leave/setup/recompute () -> `{ stamped, unchanged, onDefault, unmatched, employees }`
```

#### 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. |

### Responses

| Status | Meaning |
| --- | --- |
| `201` | `{ stamped, unchanged, onDefault, unmatched, 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/leave/setup/supervisor

**Set a person's supervisor**

`operationId: LeaveController_supervisor`

Writes `employment.supervisor` (the supervisor's employee code) and mirrors their direct reports. `supervisor: null` clears it. Leave approvals route to this person.

#### Signature

```http
POST /business-made/leave/setup/supervisor (body) -> `{ employeeId, supervisor, supervisorName }`
```

#### 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 sk. | Check `employeeSk`. |
| `400` | SELF_SUPERVISOR | Someone cannot be their own supervisor | The supervisor code is the person's own. | Pick someone else. |

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. |

### Request body

```json
{
  "employeeSk": "66f0c3a1e4b0a1b2c3d4e5f6",
  "supervisor": "EMP-1001"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | `{ employeeId, supervisor, supervisorName }` |
| `400` | Someone cannot be their own supervisor — The supervisor code is the person's own. |
| `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 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/leave/setup/opening-balances

**Preview opening balances**

`operationId: LeaveController_openingBalances`

What each person's balance per leave type would be for the year under their policy — entitled and accrued so far — next to what exists. Nothing is saved.

#### Signature

```http
GET /business-made/leave/setup/opening-balances (year?: integer) -> `{ year, unit, rows:[{employeeId, leaveTypeId, entitled, accrued, used, existing, changed, …}], summary:{people, rows, new, changed} }`
```

#### Access

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

#### Errors

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

#### See also

- `POST /business-made/leave/setup/opening-balances`

### 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. |
| `year` | query | integer | — | Default: the current year. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | `{ year, unit, rows:[{employeeId, leaveTypeId, entitled, accrued, used, existing, changed, …}], summary:{people, rows, new, changed} }` |
| `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/leave/setup/opening-balances

**Commit opening balances**

`operationId: LeaveController_commitOpeningBalances`

Commits the preview: creates missing balances and refreshes entitled/accrued on existing ones. **Used and pending are never touched** except where an override sets `used` on a new balance.

#### Signature

```http
POST /business-made/leave/setup/opening-balances (body) -> `{ year, created, updated }`
```

#### 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. |

### Request body

```json
{
  "year": 2026,
  "overrides": [
    {
      "employeeId": "EMP-1043",
      "leaveTypeId": "LT-annual",
      "used": 3
    }
  ]
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | `{ year, created, updated }` |
| `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/leave/calendar/suggest

**Suggest public holidays**

`operationId: LeaveController_suggestHolidays`

Proposes the year's public holidays for each location (or one) from its country. **Nothing is saved** — keep the ones you want with calendar/accept. Locations with no country are skipped.

#### Signature

```http
GET /business-made/leave/calendar/suggest (year?: integer, location?: string) -> `{ year, suggestions:[{location, date, title, sourceId, paid, …}], skipped, locations }`
```

#### Access

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

#### Errors

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

#### See also

- `POST /business-made/leave/calendar/accept`

### 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. |
| `year` | query | integer | — | Default: this year. |
| `location` | query | string | — | Location slug. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | `{ year, suggestions:[{location, date, title, sourceId, paid, …}], skipped, locations }` |
| `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/leave/calendar/accept

**Keep suggested holidays**

`operationId: LeaveController_acceptHolidays`

Creates the ticked suggestions. A suggestion already imported (same `sourceId`) is skipped, so re-running never undoes an edit.

#### Signature

```http
POST /business-made/leave/calendar/accept (body) -> `{ created, skipped }`
```

#### 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. |

### Request body

```json
{
  "rows": [
    {
      "location": "harbor-grill",
      "date": "2026-07-04",
      "title": "Independence Day",
      "sourceId": "US-2026-07-04"
    }
  ]
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | `{ created, skipped }` |
| `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/leave/team

**Who is off when**

`operationId: LeaveController_team`

The team-off calendar: approved and pending requests per site and day, with a coverage warning when a site would have more than 30% of its people off on a day. Default window: `from` today, 30 days.

#### Signature

```http
GET /business-made/leave/team (from?: string, to?: string, businessLocationId?: string) -> `{ from, to, location, sites, people, days:[{date, off:[…], bySite, warning?}], warnAt, requests }`
```

#### 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. |
| `from` | query | string | — |  |
| `to` | query | string | — |  |
| `businessLocationId` | query | string | — | Location; `all` = every site. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | `{ from, to, location, sites, people, days:[{date, off:[…], bySite, warning?}], warnAt, requests }` |
| `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/leave/insights

**Leave liability and usage**

`operationId: LeaveController_insights`

Read-only numbers for HR, in the org's unit: accrued liability, what is owed per type, usage by month, request counts by status and the average hours to a decision.

#### Signature

```http
GET /business-made/leave/insights (year?: integer, businessLocationId?: string) -> `{ year, unit, location, liability, owed:{amount, unit, types}, usage:[{month, requests, amount}], requests:{total, pending, approved, rejected, …}, decisionHours }`
```

#### 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. |
| `year` | query | integer | — |  |
| `businessLocationId` | query | string | — |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | `{ year, unit, location, liability, owed:{amount, unit, types}, usage:[{month, requests, amount}], requests:{total, pending, approved, rejected, …}, decisionHours }` |
| `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/leave/balances-grid

**Balances grid**

`operationId: LeaveController_balancesGrid`

One row per current person, one cell per active leave type, for the year.

#### Signature

```http
GET /business-made/leave/balances-grid (year?: integer, businessLocationId?: string) -> `{ year, unit, types, rows, people }`
```

#### 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. |
| `year` | query | integer | — |  |
| `businessLocationId` | query | string | — |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | `{ year, unit, types, rows, people }` |
| `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/leave/reports/{kind}

**Leave report rows**

`operationId: LeaveController_report`

Rows for one report — `balances`, `usage`, `pending` or `liability` (anything else falls back to `balances`) — with column definitions and footer totals of the amount columns. Sites read as their names.

#### Signature

```http
GET /business-made/leave/reports/{kind} (kind: string, year?: integer, businessLocationId?: string) -> `{ columns:[[key,label]], rows, totals, count }`
```

#### 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. |
| `kind` | path | "balances" \| "usage" \| "pending" \| "liability" | yes |  |
| `year` | query | integer | — |  |
| `businessLocationId` | query | string | — |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | `{ columns:[[key,label]], rows, totals, count }` |
| `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/leave/digest/run

**Send the approvers' digest now**

`operationId: LeaveController_digestRun`

One email per approver listing everything waiting on them. Also runs nightly. Sends nothing when the org turned the digest off (`approval.dailyDigest: false`).

#### Signature

```http
POST /business-made/leave/digest/run () -> `{ sent, waiting }` or `{ sent: 0, skipped: "digest off" }`
```

#### 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. |

### Responses

| Status | Meaning |
| --- | --- |
| `201` | `{ sent, waiting }` or `{ sent: 0, skipped: "digest off" }` |
| `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/leave/taken/run

**Mark past approved leave as taken**

`operationId: LeaveController_takenRun`

Approved leave whose last day has passed becomes `taken`. Balances already moved at approval. Also runs nightly.

#### Signature

```http
POST /business-made/leave/taken/run () -> `{ date, taken }`
```

#### 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. |

### Responses

| Status | Meaning |
| --- | --- |
| `201` | `{ date, taken }` |
| `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/leave/engine/status

**Leave scheduled jobs**

`operationId: LeaveController_engineStatus`

What is scheduled for this org — each job's cron, timezone, whether it is enabled and queued, next and last run. The jobs are configured under `leave.jobs` in the readiness settings.

#### Signature

```http
GET /business-made/leave/engine/status () -> `{ serverTime, jobs:[{name, cron, timezone, enabled, scheduled, nextRun, lastRun, warning?}], settings, escalation }`
```

#### Access

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

#### Errors

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

#### See also

- `GET /business-made/readiness/settings`

### 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` | `{ serverTime, jobs:[{name, cron, timezone, enabled, scheduled, nextRun, lastRun, warning?}], settings, escalation }` |
| `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/leave/accrual/status

**Accrual status**

`operationId: LeaveController_accrualStatus`

This year's balances count, which months have been accrued, and the last run.

#### Signature

```http
GET /business-made/leave/accrual/status () -> `{ year, balances, periodsPosted, lastRun, nextRun }`
```

#### 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. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | `{ year, balances, periodsPosted, lastRun, nextRun }` |
| `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/leave/accrual/run

**Run accrual now**

`operationId: LeaveController_accrualRun`

Brings every accrued balance up to what its policy says has been earned by `asOf` (default today). Fixed types are set once. **Idempotent per month**: one accrual transaction per balance per month; a month already posted is skipped.

#### Signature

```http
POST /business-made/leave/accrual/run (body) -> `{ period, posted, created, unchanged, rows }`
```

#### 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. |

### Request body

```json
{
  "asOf": "2026-09-30"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | `{ period, posted, created, unchanged, rows }` |
| `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/leave/accrual/rollover

**Year-end rollover**

`operationId: LeaveController_accrualRollover`

Opens next year's balances from each policy, carrying over what the policy allows from `fromYear` (default this year). A next-year balance that already exists is never touched.

#### Signature

```http
POST /business-made/leave/accrual/rollover (body) -> `{ fromYear, toYear, created, skipped, carried }`
```

#### 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. |

### Request body

```json
{
  "fromYear": 2026
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | `{ fromYear, toYear, created, skipped, carried }` |
| `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/leave/calendar

**List calendar days**

`operationId: LeaveController_listDays`

Public holidays, closures and special days (bm_calendar_day) for a year, sorted by date — optionally for one location.

#### Signature

```http
GET /business-made/leave/calendar (year?: integer, location?: string) -> Calendar days
```

#### 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. |
| `year` | query | integer | — |  |
| `location` | query | string | — | Location slug. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Calendar days |
| `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/leave/calendar

**Create or update a calendar day**

`operationId: LeaveController_saveDay`

Saves a holiday, closure or special day. With `sk` it updates that day; otherwise creates one. `kind` defaults to `holiday`, `observed` and `paid` to true; `hours` applies to a `special` day.

#### Signature

```http
POST /business-made/leave/calendar (body) -> The saved day
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `400` | DATE_TITLE_REQUIRED | A date and a name are required | `date` or `title` is missing. | Send both. |

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. |

### Request body

```json
{
  "date": "2026-12-25",
  "title": "Christmas Day",
  "kind": "holiday",
  "paid": true
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The saved day |
| `400` | A date and a name are required — `date` or `title` is missing. |
| `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/leave/calendar/{id}

**Delete a calendar day**

`operationId: LeaveController_deleteDay`

Removes a holiday, closure or special day.

#### Signature

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

#### 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. |
| `id` | path | string | yes | bm_calendar_day sk. |

### 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. |

## GET /business-made/leave/types

**List leave types**

`operationId: LeaveController_getLeaveTypes`

Every leave type defined for the org — annual, sick, parental and so on.

#### Signature

```http
GET /business-made/leave/types () -> Leave types
```

#### Access

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

#### Errors

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

#### See also

- `GET /business-made/leave/types/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. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Leave types |
| `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/leave/types

**Create a leave type**

`operationId: LeaveController_createLeaveType`

Defines a leave type. Its accrual rules are what the balance endpoints apply, so get them right before employees start accruing against it.

#### Signature

```http
POST /business-made/leave/types (body) -> The created leave type
```

#### Access

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

#### Errors

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

#### See also

- `POST /business-made/leave/balances`

### 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 leave type to create.

```json
{
  "code": "ANNUAL",
  "name": "Annual leave",
  "accrualRate": 2.08,
  "unit": "days",
  "maxCarryOver": 5
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The created leave type |
| `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/leave/types/active

**List active leave types**

`operationId: LeaveController_getActiveLeaveTypes`

Leave types currently available to request. Retired types stay on historical records but do not appear here.

#### Signature

```http
GET /business-made/leave/types/active () -> Active leave types
```

#### Access

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

#### Errors

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

#### See also

- `POST /business-made/leave/requests`

### 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 leave types |
| `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/leave/types/{id}

**Get a leave type**

`operationId: LeaveController_getLeaveType`

Fetches one leave type with its accrual and carry-over rules.

#### Signature

```http
GET /business-made/leave/types/{id} (id: string) -> The leave type
```

#### Access

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

#### Errors

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

#### See also

- `POST /business-made/leave/types/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 | Record id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The leave type |
| `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/leave/types/{id}

**Delete a leave type**

`operationId: LeaveController_deleteLeaveType`

Deletes a leave type. Balances and historical requests referencing it are not cleaned up — deactivate it instead where history matters.

#### Signature

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

#### Access

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

#### Notes

- Orphans existing balances and requests.

#### Errors

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

#### See also

- `POST /business-made/leave/types/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 | Record 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/leave/types/update

**Update a leave type**

`operationId: LeaveController_updateLeaveType`

Updates a leave type. Changing accrual rules affects **future** accruals only — balances already accrued are not recalculated. **Replaces the stored record** — send the whole record as read (`sk` plus `data`), not just the changed fields.

#### Signature

```http
POST /business-made/leave/types/update (body) -> The updated leave type
```

#### Access

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

#### Notes

- Existing balances are left as they are. Adjust them explicitly if a rule change should apply retrospectively.

#### Errors

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

#### See also

- `POST /business-made/leave/balances/{id}/adjust`

### 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 leave type to update.

```json
{
  "sk": "LT-annual",
  "data": {
    "maxCarryOver": 10
  }
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated leave type |
| `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/leave/balances

**List leave balances**

`operationId: LeaveController_getLeaveBalances`

Leave balances across the workforce — the liability figure for accrued but untaken leave.

#### Signature

```http
GET /business-made/leave/balances () -> Leave balances
```

#### Access

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

#### Errors

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

#### See also

- `GET /business-made/leave/balances/employee/{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. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Leave balances |
| `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/leave/balances

**Create a leave balance**

`operationId: LeaveController_createLeaveBalance`

Opens a balance for an employee against a leave type — typically done at hire, or when a new leave type is introduced.

#### Signature

```http
POST /business-made/leave/balances (body) -> The created balance
```

#### Access

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

#### Errors

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

#### See also

- `POST /business-made/leave/balances/{id}/accrue`

### 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 balance to create.

```json
{
  "employeeId": "EMP-4821",
  "leaveTypeId": "LT-annual",
  "balance": 25
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The created balance |
| `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/leave/balances/{id}

**Get a leave balance**

`operationId: LeaveController_getLeaveBalance`

Fetches one balance record.

#### Signature

```http
GET /business-made/leave/balances/{id} (id: string) -> The balance
```

#### 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/leave/balances/{id}/adjust`

### 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 | Record id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The balance |
| `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. |

## GET /business-made/leave/balances/employee/{employeeId}

**Get an employee's leave balances**

`operationId: LeaveController_getEmployeeBalances`

One employee's balances across every leave type — what a self-service leave screen shows.

#### Signature

```http
GET /business-made/leave/balances/employee/{employeeId} (employeeId: string) -> The employee's balances
```

#### Access

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

#### Errors

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

#### See also

- `POST /business-made/leave/requests`

### 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. |
| `year` | query | number | yes |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The employee's balances |
| `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/leave/balances/{id}/adjust

**Adjust a leave balance**

`operationId: LeaveController_adjustBalance`

Applies a manual correction to a balance — a carry-over, a buy-back, or fixing an error. Adds `adjustment` to both the adjustment total and `available`, and appends an `adjustment` transaction carrying `reason` and who made it. Record a reason: an unexplained change to someone's leave entitlement is the kind of thing that gets disputed later.

#### Signature

```http
POST /business-made/leave/balances/{id}/adjust (id: string, body) -> The adjusted balance
```

#### Access

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

#### Notes

- Signed — a negative amount reduces the balance.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | BALANCE_NOT_FOUND | Leave balance not found | No balance record has that id. | The body carries `code` and the `balanceId`. |

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

#### See also

- `POST /business-made/leave/balances/{id}/accrue`

### 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 | Record id. |

### Request body

The adjustment.

```json
{
  "adjustment": -2,
  "reason": "Correction — two days double-counted in the July accrual"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The adjusted balance |
| `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` | Leave balance not found — No balance record 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/leave/balances/{id}/accrue

**Accrue leave on one balance**

`operationId: LeaveController_accrueLeave`

Adds `hours` to the balance's accrued and available amounts, appends an `accrual` transaction and stamps `lastAccrualDate`. **Not idempotent**: calling it twice accrues twice. For the policy-driven monthly run use POST /business-made/leave/accrual/run, which is.

#### Signature

```http
POST /business-made/leave/balances/{id}/accrue (id: string, body) -> The updated balance
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | BALANCE_NOT_FOUND | Leave balance not found | No balance record has that id. | The body carries `code` and the `balanceId`. |

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

#### See also

- `POST /business-made/leave/accrual/run`

### 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 | Record id. |

### Request body

Accrual details.

```json
{
  "hours": 6.67,
  "accrualDate": "2026-09-30"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated balance |
| `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` | Leave balance not found — No balance record 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/leave/requests

**List leave requests**

`operationId: LeaveController_getLeaveRequests`

Leave requests across the org.

#### Signature

```http
GET /business-made/leave/requests () -> Leave requests
```

#### Access

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

#### Errors

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

#### See also

- `GET /business-made/leave/requests/pending-approvals`

### 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` | Leave requests |
| `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/leave/requests

**Create a leave request**

`operationId: LeaveController_createLeaveRequest`

Creates a leave request as a **draft**. It is not visible to approvers until submitted, so a half-filled request does not appear in the queue.

#### Signature

```http
POST /business-made/leave/requests (body) -> The created request
```

#### Access

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

#### Notes

- Creating does not submit. Call `submit` to send it for approval.

#### Errors

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

#### See also

- `POST /business-made/leave/requests/{id}/submit`

### 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 request.

```json
{
  "employeeId": "EMP-4821",
  "leaveTypeId": "LT-annual",
  "startDate": "2026-10-05",
  "endDate": "2026-10-09"
}
```

### 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. |
| `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/leave/requests/pending-approvals

**List requests awaiting approval**

`operationId: LeaveController_getPendingApprovals`

The approval queue — submitted requests that nobody has decided on yet.

#### Signature

```http
GET /business-made/leave/requests/pending-approvals () -> Pending requests
```

#### Access

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

#### Errors

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

#### See also

- `POST /business-made/leave/requests/{id}/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. |
| `approver` | query | string | yes |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Pending requests |
| `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/leave/requests/{id}

**Get a leave request**

`operationId: LeaveController_getLeaveRequest`

Fetches one request with its dates, type and status.

#### Signature

```http
GET /business-made/leave/requests/{id} (id: string) -> The request
```

#### 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/leave/requests/{id}/submit`

### 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 | Record id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The request |
| `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/leave/requests/{id}

**Delete a leave request**

`operationId: LeaveController_deleteLeaveRequest`

Deletes a request outright. It does not release a held balance or close its approval task — cancel instead, which does both and keeps the record of what was asked for.

#### Signature

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

#### Access

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

#### Errors

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

#### See also

- `POST /business-made/leave/requests/{id}/cancel`

### 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 | Record 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/leave/requests/update

**Update a leave request**

`operationId: LeaveController_updateLeaveRequest`

Updates a request. Editing one that has already been approved does not re-run approval — cancel and raise a new request instead. **Replaces the stored record** — send the whole record as read (`sk` plus `data`), not just the changed fields.

#### Signature

```http
POST /business-made/leave/requests/update (body) -> The updated request
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | REQUEST_NOT_FOUND | Leave request not found | No leave request has that id. | The body carries `code` and the id. |

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

#### See also

- `POST /business-made/leave/requests/{id}/cancel`

### 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 request to update.

```json
{
  "sk": "LR-4821",
  "data": {
    "endDate": "2026-10-10"
  }
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated 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` | Leave request not found — No leave request 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/leave/requests/{id}/submit

**Submit a leave request**

`operationId: LeaveController_submitLeaveRequest`

Sends a draft (or a declined / cancelled one — a resubmission starts a new round) for approval. The server works out the cost in the org's unit (working days only — holidays and closures are skipped), resolves who decides (supervisor, site manager or HR by the approval flow; after a decline it goes back to whoever declined), **holds** the amount on the balance, opens a task on the `leave-approval` workflow and emails the approver and the employee. Blackout periods and short notice are shown to the approver, never blocked.

#### Signature

```http
POST /business-made/leave/requests/{id}/submit (id: string) -> The request, now `pending`, with `cost`, `approver` and `approvalChain`
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | REQUEST_NOT_FOUND | Leave request not found | No leave request has that id. | The body carries `code` and the id. |
| `400` | BAD_STATUS | A pending request cannot be submitted | The request is pending or approved already. | Nothing to do, or cancel it first. |

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

#### See also

- `POST /business-made/leave/requests/{id}/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 | Record id. |

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The request, now `pending`, with `cost`, `approver` and `approvalChain` |
| `400` | A pending request cannot be submitted — The request is pending or approved already. |
| `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` | Leave request not found — No leave request 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/leave/requests/{id}/edit

**Edit a request before sending it (again)**

`operationId: LeaveController_editLeaveRequest`

Changes a **draft, declined or cancelled** request — dates, type, reason, half day. Only the fields sent change. A pending or approved request is refused: cancel it first.

#### Signature

```http
POST /business-made/leave/requests/{id}/edit (id: string, body) -> The request after the edit
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | REQUEST_NOT_FOUND | Leave request not found | No leave request has that id. | The body carries `code` and the id. |
| `400` | BAD_STATUS | A pending request cannot be edited — cancel it first | The request is not draft, rejected or cancelled (the status is named in the message). | Cancel it, then edit and resubmit. |

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

#### See also

- `POST /business-made/leave/requests/{id}/resubmit`

### 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 | Record id. |

### Request body

```json
{
  "startDate": "2026-10-06",
  "endDate": "2026-10-09"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The request after the edit |
| `400` | A pending request cannot be edited — cancel it first — The request is not draft, rejected or cancelled (the status is named in the message). |
| `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` | Leave request not found — No leave request 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/leave/requests/{id}/resubmit

**Send a request again**

`operationId: LeaveController_resubmitLeaveRequest`

Sends a **declined** request, one withdrawn before any decision, or a draft back for approval — the same request, next round, with a new approval task. Optional edits in the body are applied first (same fields as edit). The approver sees who declined it last time and why.

#### Signature

```http
POST /business-made/leave/requests/{id}/resubmit (id: string, body) -> The request, pending again
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | REQUEST_NOT_FOUND | Leave request not found | No leave request has that id. | The body carries `code` and the id. |
| `400` | CANNOT_RESUBMIT | Only a declined request, or one withdrawn before a decision, can be sent again — make a new request instead | The request is pending or approved, or was cancelled after an approval. | Create a new request. |

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

#### See also

- `POST /business-made/leave/requests/{id}/edit`

### 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 | Record id. |

### Request body

```json
{
  "reason": "Moved a day later — cover is arranged"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The request, pending again |
| `400` | Only a declined request, or one withdrawn before a decision, can be sent again — make a new request instead — The request is pending or approved, or was cancelled after an approval. |
| `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` | Leave request not found — No leave request 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/leave/requests/{id}/approve

**Approve a leave request**

`operationId: LeaveController_approveLeaveRequest`

Approves a pending request and moves its approval task. When the policy needs two levels, a first-level approval hands the request to HR for the final say instead of finishing it. The final approval uses the held amount from the balance and flags the person's shifts on those days for cover.

#### Signature

```http
POST /business-made/leave/requests/{id}/approve (id: string, body) -> The request after the decision
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | REQUEST_NOT_FOUND | Leave request not found | No leave request has that id. | The body carries `code` and the id. |
| `400` | BAD_STATUS | This request is approved, not waiting on a decision | The request is not pending. | Nothing to decide. |

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

#### See also

- `POST /business-made/leave/requests/{id}/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 | Record id. |

### Request body

Optional note (`note` or `comments`).

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

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The request after the decision |
| `400` | This request is approved, not waiting on a decision — The request is not pending. |
| `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` | Leave request not found — No leave request 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/leave/requests/{id}/reject

**Reject a leave request**

`operationId: LeaveController_rejectLeaveRequest`

Declines a pending request with a reason the employee sees; the held amount goes back to the balance. The person can edit and resubmit it — it then returns to whoever declined.

#### Signature

```http
POST /business-made/leave/requests/{id}/reject (id: string, body) -> The request after the decision
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | REQUEST_NOT_FOUND | Leave request not found | No leave request has that id. | The body carries `code` and the id. |
| `400` | BAD_STATUS | This request is approved, not waiting on a decision | The request is not pending. | Nothing to decide. |

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

#### See also

- `POST /business-made/leave/requests/{id}/resubmit`

### 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 | Record id. |

### Request body

Why it was declined (`note` or `reason`). Required.

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

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The request after the decision |
| `400` | This request is approved, not waiting on a decision — The request is not pending. |
| `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` | Leave request not found — No leave request 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/leave/requests/{id}/cancel

**Cancel a leave request**

`operationId: LeaveController_cancelLeaveRequest`

Withdraws a request and closes its approval task. A pending request releases its held amount; an approved one returns the used amount to the balance. The approver (pending) or the employee (approved, when someone else cancelled) is told.

#### Signature

```http
POST /business-made/leave/requests/{id}/cancel (id: string, body) -> The cancelled request
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | REQUEST_NOT_FOUND | Leave request not found | No leave request has that id. | The body carries `code` and the id. |
| `400` | BAD_STATUS | This request is already cancelled | The request is already cancelled or rejected. | Nothing to do. |

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

#### See also

- `POST /business-made/leave/requests/{id}/resubmit`

### 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 | Record id. |

### Request body

Why it was cancelled.

```json
{
  "reason": "Plans changed"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The cancelled request |
| `400` | This request is already cancelled — The request is already cancelled or rejected. |
| `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` | Leave request not found — No leave request 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/leave/requests/employee/{employeeId}

**Get an employee's leave requests**

`operationId: LeaveController_getEmployeeRequests`

One employee's leave history and pending requests.

#### Signature

```http
GET /business-made/leave/requests/employee/{employeeId} (employeeId: string) -> The employee's requests
```

#### Access

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

#### Errors

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

#### See also

- `GET /business-made/leave/balances/employee/{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. |
| `status` | query | string | yes |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The employee's requests |
| `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/leave/policies

**List leave policies**

`operationId: LeaveController_getLeavePolicies`

The leave policies defined for the org — the rules governing entitlement and approval.

#### Signature

```http
GET /business-made/leave/policies () -> Leave policies
```

#### Access

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

#### Errors

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

#### See also

- `GET /business-made/leave/policies/{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. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Leave policies |
| `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/leave/policies

**Create a leave policy**

`operationId: LeaveController_createLeavePolicy`

Defines a leave policy.

#### Signature

```http
POST /business-made/leave/policies (body) -> The created policy
```

#### Access

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

#### Errors

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

#### See also

- `POST /business-made/leave/policies/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. |

### Request body

The policy to create.

```json
{
  "name": "UK standard",
  "minNoticeDays": 14,
  "maxConsecutiveDays": 15
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The created policy |
| `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/leave/policies/{id}

**Get a leave policy**

`operationId: LeaveController_getLeavePolicy`

Fetches one leave policy.

#### Signature

```http
GET /business-made/leave/policies/{id} (id: string) -> The policy
```

#### Access

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

#### Errors

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

#### See also

- `POST /business-made/leave/policies/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 | Record id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The policy |
| `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/leave/policies/{id}

**Delete a leave policy**

`operationId: LeaveController_deleteLeavePolicy`

Deletes a leave policy.

#### Signature

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

#### Access

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

#### Errors

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

#### See also

- `GET /business-made/leave/policies`

### 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 | Record 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/leave/policies/update

**Update a leave policy**

`operationId: LeaveController_updateLeavePolicy`

Updates a leave policy. Applies to future requests; approved leave is unaffected. **Replaces the stored record** — send the whole record as read (`sk` plus `data`), not just the changed fields.

#### Signature

```http
POST /business-made/leave/policies/update (body) -> The updated policy
```

#### Access

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

#### Errors

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

#### See also

- `GET /business-made/leave/policies`

### 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 policy to update.

```json
{
  "sk": "LP-uk",
  "data": {
    "minNoticeDays": 21
  }
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated policy |
| `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/leave/metrics

**Get leave metrics**

`operationId: LeaveController_getLeaveMetrics`

Aggregate leave figures — utilisation, outstanding liability and absence patterns.

#### Signature

```http
GET /business-made/leave/metrics () -> Leave metrics
```

#### Access

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

#### Errors

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

#### See also

- `GET /business-made/leave/balances`

### 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. |
| `year` | query | number | yes |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Leave metrics |
| `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. |

