# Business Made · Compensation

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

**Compensation changes for the HR screen**

`operationId: CompensationController_viewChanges`

Every change as a finished row (person, current and new pay labels, status), newest first, with counts by status and a summary: how many are waiting for approval, approved but not yet applied to pay, and drafts, plus a one-line headline. `status` filters the rows (`all` = no filter); counts always cover everything.

#### Signature

```http
GET /business-made/compensation/view/changes (status?: string) -> `{ rows, counts, summary:{waiting, readyToApply, drafts, headline} }`
```

#### Access

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

#### Errors

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

#### See also

- `GET /business-made/compensation/view/changes/{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. |
| `status` | query | "all" \| "draft" \| "pending-approval" \| "approved" \| "processed" \| "rejected" \| "cancelled" | — |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | `{ rows, counts, summary:{waiting, readyToApply, drafts, headline} }` |
| `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/compensation/view/changes/{id}

**One compensation change for the HR screen**

`operationId: CompensationController_viewChange`

The change's row plus reason, notes, department, what the person is paid today (`currentPayLabel`, `currentPaySource`), a timeline (drafted, sent, approved/declined, applied, cancelled) and, for a draft, `edit` — the values to prefill the form.

#### Signature

```http
GET /business-made/compensation/view/changes/{id} (id: string) -> The change detail
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | COMP_CHANGE_NOT_FOUND | Compensation change not found. | No compensation change has that id. | The body carries `code: "COMP_CHANGE_NOT_FOUND"` and the id. |

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

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The change detail |
| `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` | Compensation change not found. — No compensation change 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/compensation/view/bonuses

**Bonuses for the HR screen**

`operationId: CompensationController_viewBonuses`

Every bonus as a finished row, newest first, with counts by status and a summary: waiting for approval, the approved-not-yet-paid total (`owedLabel`) and what was paid this year. `status` filters the rows.

#### Signature

```http
GET /business-made/compensation/view/bonuses (status?: string) -> `{ rows, counts, summary:{waiting, owedLabel, paidThisYearLabel, year, headline} }`
```

#### 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. |
| `status` | query | "all" \| "draft" \| "pending-approval" \| "approved" \| "scheduled" \| "paid" \| "rejected" \| "cancelled" | — |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | `{ rows, counts, summary:{waiting, owedLabel, paidThisYearLabel, year, headline} }` |
| `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/compensation/view/bonuses/{id}

**One bonus for the HR screen**

`operationId: CompensationController_viewBonus`

The bonus row plus description, notes, a timeline (drafted, sent, approved/declined, paid, cancelled) and, for a draft, `edit` values.

#### Signature

```http
GET /business-made/compensation/view/bonuses/{id} (id: string) -> The bonus detail
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | BONUS_NOT_FOUND | Bonus not found. | No bonus has that id. | The body carries `code: "BONUS_NOT_FOUND"` and the id. |

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

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The bonus detail |
| `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` | Bonus not found. — No bonus 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/compensation/view/grades

**Salary grades for the HR screen**

`operationId: CompensationController_viewGrades`

Every grade as a finished row with counts by status. `status` filters the rows.

#### Signature

```http
GET /business-made/compensation/view/grades (status?: string) -> `{ rows, counts }`
```

#### 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. |
| `status` | query | "all" \| "draft" \| "active" \| "inactive" \| "archived" | — |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | `{ rows, counts }` |
| `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/compensation/view/grades/{id}

**One salary grade for the HR screen**

`operationId: CompensationController_viewGrade`

The grade row plus its raw `data`.

#### Signature

```http
GET /business-made/compensation/view/grades/{id} (id: string) -> The grade detail
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | SALARY_GRADE_NOT_FOUND | Salary grade not found. | No salary grade has that id. | The body carries `code: "SALARY_GRADE_NOT_FOUND"` and the id. |

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

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The grade detail |
| `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` | Salary grade not found. — No salary grade 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/compensation/view/pay/{employeeId}

**What a person is paid today**

`operationId: CompensationController_viewPay`

Shown on the new-change form before anything is typed: pay type, hourly rate or base salary, a label, and where it came from (payroll profile or employee record). `employeeId` may be the bm_employee sk or the employee code.

#### Signature

```http
GET /business-made/compensation/view/pay/{employeeId} (employeeId: string) -> `{ employeeSk, name, title, payType, hourlyRate, baseSalary, label, source, … }`
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | EMPLOYEE_NOT_FOUND | Person not found. | No person has that id. | The body carries `code: "EMPLOYEE_NOT_FOUND"` and the id. |

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. |
| `employeeId` | path | string | yes | Employee id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | `{ employeeSk, name, title, payType, hourlyRate, baseSalary, label, source, … }` |
| `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` | Person not found. — No person 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/compensation/changes/{id}/cancel

**Cancel a compensation change**

`operationId: CompensationController_cancelChange`

Cancels a draft, pending, submitted or approved change (not one already processed), recording who and when.

#### Signature

```http
POST /business-made/compensation/changes/{id}/cancel (id: string) -> The cancelled change
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | COMP_CHANGE_NOT_FOUND | Compensation change not found. | No compensation change has that id. | The body carries `code: "COMP_CHANGE_NOT_FOUND"` and the id. |
| `400` | NOT_CANCELLABLE | This change can no longer be cancelled. | The change is processed, declined or already cancelled. | — |

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

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The cancelled change |
| `400` | This change can no longer be cancelled. — The change is processed, declined or already cancelled. |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Compensation change not found. — No compensation change 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/compensation/bonuses/{id}/reject

**Decline a bonus**

`operationId: CompensationController_rejectBonus`

Declines a bonus waiting for approval, recording who, when and the optional reason.

#### Signature

```http
POST /business-made/compensation/bonuses/{id}/reject (id: string, body) -> The declined bonus
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | BONUS_NOT_FOUND | Bonus not found. | No bonus has that id. | The body carries `code: "BONUS_NOT_FOUND"` and the id. |
| `400` | NOT_PENDING | Only a bonus waiting for approval can be declined. | The bonus is not pending approval. | — |

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

### Request body

```json
{
  "reason": "Outside the bonus budget this quarter"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The declined bonus |
| `400` | Only a bonus waiting for approval can be declined. — The bonus is not pending 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` | Bonus not found. — No bonus 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/compensation/bonuses/{id}/cancel

**Cancel a bonus**

`operationId: CompensationController_cancelBonus`

Cancels a draft, pending, approved or scheduled bonus (not one already paid), recording who and when.

#### Signature

```http
POST /business-made/compensation/bonuses/{id}/cancel (id: string) -> The cancelled bonus
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | BONUS_NOT_FOUND | Bonus not found. | No bonus has that id. | The body carries `code: "BONUS_NOT_FOUND"` and the id. |
| `400` | NOT_CANCELLABLE | This bonus can no longer be cancelled. | The bonus is paid, declined or already cancelled. | — |

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

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The cancelled bonus |
| `400` | This bonus can no longer be cancelled. — The bonus is paid, declined or already cancelled. |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `404` | Bonus not found. — No bonus 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/compensation/grades/{id}/deactivate

**Deactivate a salary grade**

`operationId: CompensationController_deactivateGrade`

Sets the grade `inactive` — kept on record, not offered for new assignments.

#### Signature

```http
POST /business-made/compensation/grades/{id}/deactivate (id: string) -> The updated grade
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | SALARY_GRADE_NOT_FOUND | Salary grade not found. | No salary grade has that id. | The body carries `code: "SALARY_GRADE_NOT_FOUND"` and the id. |

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

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated grade |
| `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` | Salary grade not found. — No salary grade 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/compensation/grades/{id}/archive

**Archive a salary grade**

`operationId: CompensationController_archiveGrade`

Sets the grade `archived`.

#### Signature

```http
POST /business-made/compensation/grades/{id}/archive (id: string) -> The updated grade
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | SALARY_GRADE_NOT_FOUND | Salary grade not found. | No salary grade has that id. | The body carries `code: "SALARY_GRADE_NOT_FOUND"` and the id. |

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

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated grade |
| `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` | Salary grade not found. — No salary grade 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/compensation/grades

**List salary grades**

`operationId: CompensationController_getSalaryGrades`

The salary bands defined for the org — the ranges compensation changes are checked against.

#### Signature

```http
GET /business-made/compensation/grades () -> Salary grades
```

#### Access

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

#### Errors

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

#### See also

- `GET /business-made/compensation/grades/code/{code}`

### 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` | Salary grades |
| `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/compensation/grades

**Create a salary grade**

`operationId: CompensationController_createSalaryGrade`

Defines a salary band (bm_salary_grade), as a **draft** unless `status` is given. The code must be unique; set a salary range, an hourly range or both.

#### Signature

```http
POST /business-made/compensation/grades (body) -> The created grade
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `400` | GRADE_CODE_REQUIRED | Give the grade a code, e.g. L1 or M2. | `code` is missing. | — |

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

#### See also

- `POST /business-made/compensation/grades/{id}/activate`

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

```json
{
  "code": "G7",
  "title": "Senior Engineer",
  "level": 7,
  "salaryRange": {
    "minimum": 80000,
    "midpoint": 92000,
    "maximum": 104000
  }
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The created grade |
| `400` | Give the grade a code, e.g. L1 or M2. — `code` 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. |

## GET /business-made/compensation/grades/{id}

**Get a salary grade**

`operationId: CompensationController_getSalaryGrade`

Fetches one salary grade record (bm_salary_grade) with its salary and/or hourly range.

#### Signature

```http
GET /business-made/compensation/grades/{id} (id: string) -> The grade, or null when no grade has that id
```

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

- `GET /business-made/compensation/view/grades/{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. |
| `id` | path | string | yes | Record id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The grade, or null when no grade has that id |
| `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/compensation/grades/{id}

**Delete a salary grade**

`operationId: CompensationController_deleteSalaryGrade`

Deletes a grade. Positions and employees assigned to it are not reassigned.

#### Signature

```http
DELETE /business-made/compensation/grades/{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/compensation/grades`

### 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/compensation/grades/update

**Update a salary grade**

`operationId: CompensationController_updateSalaryGrade`

Edits a grade (body is the record: `sk` plus `data`). The same checks as create run. Existing salaries are unaffected — moving a band does not move anyone within it.

#### Signature

```http
POST /business-made/compensation/grades/update (body) -> The updated grade
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | SALARY_GRADE_NOT_FOUND | Salary grade not found | No salary grade has that id. | The body carries `code: "SALARY_GRADE_NOT_FOUND"` and the id. |
| `400` | GRADE_CODE_REQUIRED | Give the grade a code, e.g. L1 or M2. | `code` is missing. | — |

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

#### See also

- `GET /business-made/compensation/view/grades`

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

```json
{
  "sk": "66f0c3a1e4b0a1b2c3d4e5f6",
  "data": {
    "code": "G7",
    "title": "Senior Engineer",
    "level": 7,
    "salaryRange": {
      "minimum": 82000,
      "maximum": 106000
    }
  }
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated grade |
| `400` | Give the grade a code, e.g. L1 or M2. — `code` 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. |
| `404` | Salary grade not found — No salary grade 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/compensation/grades/{id}/activate

**Activate a salary grade**

`operationId: CompensationController_activateSalaryGrade`

Brings a grade into use so it can be assigned. Grades can be drafted before a pay review and activated when the new structure takes effect.

#### Signature

```http
POST /business-made/compensation/grades/{id}/activate (id: string) -> The activated grade
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | SALARY_GRADE_NOT_FOUND | Salary grade not found | No salary grade has that id. | The body carries `code: "SALARY_GRADE_NOT_FOUND"` and the id. |

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

#### See also

- `GET /business-made/compensation/grades`

### 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 activated grade |
| `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` | Salary grade not found — No salary grade 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/compensation/grades/code/{code}

**Get a salary grade by code**

`operationId: CompensationController_getSalaryGradeByCode`

Resolves a grade from its business code rather than its record id.

#### Signature

```http
GET /business-made/compensation/grades/code/{code} (code: string) -> The grade, or null when no grade has that code
```

#### Access

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

#### Errors

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

#### See also

- `GET /business-made/compensation/grades/{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. |
| `code` | path | string | yes | Grade code. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The grade, or null when no grade has that code |
| `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/compensation/changes

**List compensation changes**

`operationId: CompensationController_getCompensationChanges`

Pay changes across the org, at any stage of the approval lifecycle.

#### Signature

```http
GET /business-made/compensation/changes () -> Compensation changes
```

#### Access

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

#### Errors

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

#### See also

- `GET /business-made/compensation/changes/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` | Compensation changes |
| `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/compensation/changes

**Create a compensation change**

`operationId: CompensationController_createCompensationChange`

Proposes a pay change as a **draft**. The server fills in the person, their current pay (`previousCompensation`, read from payroll while the change is open), the change amount and percentage, and the fiscal year. It does not affect pay until submitted, approved and processed — three deliberate steps between proposing a raise and someone being paid it.

#### Signature

```http
POST /business-made/compensation/changes (body) -> The draft change
```

#### Access

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

#### Notes

- Creating changes nothing. Payroll only sees it after `process`.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `400` | EMPLOYEE_REQUIRED | Pick the person this change is for. | Neither `employeeSk` nor `employeeId` names a person. | — |

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

#### See also

- `POST /business-made/compensation/changes/{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 proposed change.

```json
{
  "employeeId": "EMP-4821",
  "changeType": "promotion",
  "effectiveDate": "2026-10-01",
  "newCompensation": {
    "payType": "salary",
    "baseSalary": 88000
  },
  "reason": "Promotion to Senior Engineer"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The draft change |
| `400` | Pick the person this change is for. — Neither `employeeSk` nor `employeeId` names a person. |
| `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/compensation/changes/{id}

**Get a compensation change**

`operationId: CompensationController_getCompensationChange`

Fetches one pay change record with its previous and new compensation and approval state.

#### Signature

```http
GET /business-made/compensation/changes/{id} (id: string) -> The change, or null when none has that id
```

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

- `GET /business-made/compensation/view/changes/{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. |
| `id` | path | string | yes | Record id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The change, or null when none has that id |
| `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/compensation/changes/{id}

**Delete a compensation change**

`operationId: CompensationController_deleteCompensationChange`

Deletes a draft, declined or cancelled change. One waiting for approval, approved or processed is part of someone's pay history and is refused — cancel it instead.

#### Signature

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

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `400` | KEEP_HISTORY | This change is part of someone's pay history — cancel it instead of deleting it. | The change is pending approval, approved or processed. | — |

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

#### See also

- `POST /business-made/compensation/changes/{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 |
| `400` | This change is part of someone's pay history — cancel it instead of deleting it. — The change is pending approval, approved or processed. |
| `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/compensation/changes/update

**Edit a draft compensation change**

`operationId: CompensationController_updateCompensationChange`

Edits a **draft** change (body: `sk` plus the fields, same as create); the derived fields are recomputed. Anything past draft is refused — cancel it and raise a new one.

#### Signature

```http
POST /business-made/compensation/changes/update (body) -> The updated change
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | COMP_CHANGE_NOT_FOUND | Compensation change not found | No compensation change has that id. | The body carries `code: "COMP_CHANGE_NOT_FOUND"` and the id. |
| `400` | NOT_DRAFT | Only a draft change can be edited — cancel it and raise a new one. | The change is past draft. | — |

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

#### See also

- `POST /business-made/compensation/changes/{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 change to update.

```json
{
  "sk": "66f0c3a1e4b0a1b2c3d4e5f6",
  "effectiveDate": "2026-11-01",
  "newCompensation": {
    "baseSalary": 90000
  }
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated change |
| `400` | Only a draft change can be edited — cancel it and raise a new one. — The change is past draft. |
| `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` | Compensation change not found — No compensation change 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/compensation/changes/{id}/submit

**Submit a compensation change**

`operationId: CompensationController_submitForApproval`

Sends a draft pay change for approval (`pending-approval`).

#### Signature

```http
POST /business-made/compensation/changes/{id}/submit (id: string) -> The submitted change
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | COMP_CHANGE_NOT_FOUND | Compensation change not found | No compensation change has that id. | The body carries `code: "COMP_CHANGE_NOT_FOUND"` and the id. |
| `400` | NOT_DRAFT | Only a draft change can be sent for approval. | The change is not a draft. | — |

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

#### See also

- `POST /business-made/compensation/changes/{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 submitted change |
| `400` | Only a draft change can be sent for approval. — The change is not a draft. |
| `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` | Compensation change not found — No compensation change 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/compensation/changes/{id}/approve

**Approve a compensation change**

`operationId: CompensationController_approveCompensationChange`

Approves a change waiting for approval. **Approval alone does not change anyone's pay** — the change must still be processed.

#### Signature

```http
POST /business-made/compensation/changes/{id}/approve (id: string, body) -> The approved change
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | COMP_CHANGE_NOT_FOUND | Compensation change not found | No compensation change has that id. | The body carries `code: "COMP_CHANGE_NOT_FOUND"` and the id. |
| `400` | NOT_PENDING | Only a change waiting for approval can be approved. | The change is not pending approval. | — |

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

#### See also

- `POST /business-made/compensation/changes/{id}/process`

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

```json
{
  "comments": "Within the review budget"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The approved change |
| `400` | Only a change waiting for approval can be approved. — The change is not pending 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` | Compensation change not found — No compensation change 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/compensation/changes/{id}/reject

**Decline a compensation change**

`operationId: CompensationController_rejectCompensationChange`

Declines a change waiting for approval, with a reason.

#### Signature

```http
POST /business-made/compensation/changes/{id}/reject (id: string, body) -> The declined change
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | COMP_CHANGE_NOT_FOUND | Compensation change not found | No compensation change has that id. | The body carries `code: "COMP_CHANGE_NOT_FOUND"` and the id. |
| `400` | NOT_PENDING | Only a change waiting for approval can be declined. | The change is not pending approval. | — |

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

#### See also

- `POST /business-made/compensation/changes`

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

```json
{
  "reason": "Above band maximum for the grade"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The declined change |
| `400` | Only a change waiting for approval can be declined. — The change is not pending 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` | Compensation change not found — No compensation change 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/compensation/changes/{id}/process

**Process a compensation change**

`operationId: CompensationController_processCompensationChange`

Applies an approved pay change: writes the new pay type and rate or salary (and pay frequency) onto the **employee record** and, when the person has one, their **payroll profile**; `appliedTo` records which. **This is the step that actually changes what someone is paid.**

Recalculate any open payroll run afterwards — a run already calculated keeps the old rate until it is.

#### Signature

```http
POST /business-made/compensation/changes/{id}/process (id: string) -> The processed change, with `appliedTo`
```

#### Access

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

#### Notes

- Recalculate any in-flight payroll run, or it will pay the previous salary.

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | COMP_CHANGE_NOT_FOUND | Compensation change not found | No compensation change has that id. | The body carries `code: "COMP_CHANGE_NOT_FOUND"` and the id. |
| `400` | NOT_APPROVED | Compensation change must be approved before processing | The change is not approved; the body carries its `status`. | — |

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

#### See also

- `POST /business-made/payroll/runs/{id}/calculate`

### 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 processed change, with `appliedTo` |
| `400` | Compensation change must be approved before processing — The change is not approved; the body carries its `status`. |
| `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` | Compensation change not found — No compensation change 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/compensation/changes/employee/{employeeId}

**Get an employee's compensation history**

`operationId: CompensationController_getEmployeeCompensationHistory`

Every pay change for one employee — their salary history and the reasons behind it.

#### Signature

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

#### Access

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

#### Notes

- Highly sensitive. Restrict to HR and the employee's own management line.

#### Errors

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

#### See also

- `GET /business-made/compensation/changes`

### Parameters

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

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The employee's compensation changes |
| `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/compensation/bonuses

**List bonuses**

`operationId: CompensationController_getBonuses`

Bonuses across the org at any stage of their lifecycle.

#### Signature

```http
GET /business-made/compensation/bonuses () -> Bonuses
```

#### Access

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

#### Errors

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

#### See also

- `GET /business-made/compensation/bonuses/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` | Bonuses |
| `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/compensation/bonuses

**Create a bonus**

`operationId: CompensationController_createBonus`

Proposes a bonus as a **draft**; the server fills in the person and rounds the amount. It goes through submit → approve → schedule → paid before any money moves.

#### Signature

```http
POST /business-made/compensation/bonuses (body) -> The draft bonus
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `400` | EMPLOYEE_REQUIRED | Pick the person this bonus is for. | Neither `employeeSk` nor `employeeId` names a person. | — |

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

#### See also

- `POST /business-made/compensation/bonuses/{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 bonus to propose.

```json
{
  "employeeId": "EMP-4821",
  "bonusType": "performance",
  "title": "H1 performance bonus",
  "amount": 5000
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The draft bonus |
| `400` | Pick the person this bonus is for. — Neither `employeeSk` nor `employeeId` names a person. |
| `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/compensation/bonuses/{id}

**Get a bonus**

`operationId: CompensationController_getBonus`

Fetches one bonus record with its amount and status.

#### Signature

```http
GET /business-made/compensation/bonuses/{id} (id: string) -> The bonus, or null when none has that id
```

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

- `GET /business-made/compensation/view/bonuses/{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. |
| `id` | path | string | yes | Record id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The bonus, or null when none has that id |
| `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/compensation/bonuses/{id}

**Delete a bonus**

`operationId: CompensationController_deleteBonus`

Deletes a draft, declined or cancelled bonus. One pending approval, approved, scheduled or paid is part of someone's pay history and is refused — cancel it instead.

#### Signature

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

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `400` | KEEP_HISTORY | This bonus is part of someone's pay history — cancel it instead of deleting it. | The bonus is pending approval, approved, scheduled or paid. | — |

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

#### See also

- `POST /business-made/compensation/bonuses/{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 |
| `400` | This bonus is part of someone's pay history — cancel it instead of deleting it. — The bonus is pending approval, approved, scheduled or paid. |
| `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/compensation/bonuses/update

**Edit a draft bonus**

`operationId: CompensationController_updateBonus`

Edits a **draft** bonus (body: `sk` plus the fields, same as create). Anything past draft is refused — cancel it and raise a new one.

#### Signature

```http
POST /business-made/compensation/bonuses/update (body) -> The updated bonus
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | BONUS_NOT_FOUND | Bonus not found | No bonus has that id. | The body carries `code: "BONUS_NOT_FOUND"` and the id. |
| `400` | NOT_DRAFT | Only a draft bonus can be edited — cancel it and raise a new one. | The bonus is past draft. | — |

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

#### See also

- `POST /business-made/compensation/bonuses/{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 bonus to update.

```json
{
  "sk": "66f0c3a1e4b0a1b2c3d4e5f6",
  "amount": 6000
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The updated bonus |
| `400` | Only a draft bonus can be edited — cancel it and raise a new one. — The bonus is past draft. |
| `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` | Bonus not found — No bonus 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/compensation/bonuses/{id}/submit

**Submit a bonus**

`operationId: CompensationController_submitBonusForApproval`

Sends a draft bonus for approval.

#### Signature

```http
POST /business-made/compensation/bonuses/{id}/submit (id: string) -> The submitted bonus
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | BONUS_NOT_FOUND | Bonus not found | No bonus has that id. | The body carries `code: "BONUS_NOT_FOUND"` and the id. |
| `400` | NOT_DRAFT | Only a draft bonus can be sent for approval. | The bonus is not a draft. | — |

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

#### See also

- `POST /business-made/compensation/bonuses/{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 submitted bonus |
| `400` | Only a draft bonus can be sent for approval. — The bonus is not a draft. |
| `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` | Bonus not found — No bonus 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/compensation/bonuses/{id}/approve

**Approve a bonus**

`operationId: CompensationController_approveBonus`

Approves a bonus waiting for approval. It still needs scheduling before it reaches a payroll run.

#### Signature

```http
POST /business-made/compensation/bonuses/{id}/approve (id: string) -> The approved bonus
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | BONUS_NOT_FOUND | Bonus not found | No bonus has that id. | The body carries `code: "BONUS_NOT_FOUND"` and the id. |
| `400` | NOT_PENDING | Only a bonus waiting for approval can be approved. | The bonus is not pending approval. | — |

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

#### See also

- `POST /business-made/compensation/bonuses/{id}/schedule`

### 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 approved bonus |
| `400` | Only a bonus waiting for approval can be approved. — The bonus is not pending 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` | Bonus not found — No bonus 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/compensation/bonuses/{id}/schedule

**Schedule a bonus**

`operationId: CompensationController_scheduleBonus`

Sets the date an approved (or already scheduled) bonus will be paid, putting it into the run that covers that period.

#### Signature

```http
POST /business-made/compensation/bonuses/{id}/schedule (id: string, body) -> The scheduled bonus
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | BONUS_NOT_FOUND | Bonus not found | No bonus has that id. | The body carries `code: "BONUS_NOT_FOUND"` and the id. |
| `400` | NOT_APPROVED | Only an approved bonus can be scheduled. | The bonus is not approved or scheduled. | — |

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

#### See also

- `POST /business-made/compensation/bonuses/{id}/paid`

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

When to pay it.

```json
{
  "paymentDate": "2026-01-20"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The scheduled bonus |
| `400` | Only an approved bonus can be scheduled. — The bonus is not approved or scheduled. |
| `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` | Bonus not found — No bonus 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/compensation/bonuses/{id}/paid

**Mark a bonus paid**

`operationId: CompensationController_markBonusPaid`

Records that an approved or scheduled bonus has been paid, closing it, optionally with the pay stub and payroll run that carried it.

#### Signature

```http
POST /business-made/compensation/bonuses/{id}/paid (id: string, body) -> The paid bonus
```

#### Access

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

#### Errors

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `404` | BONUS_NOT_FOUND | Bonus not found | No bonus has that id. | The body carries `code: "BONUS_NOT_FOUND"` and the id. |
| `400` | NOT_APPROVED | Only an approved or scheduled bonus can be marked paid. | The bonus is not approved or scheduled. | — |

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

#### See also

- `GET /business-made/compensation/view/bonuses`

### 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
{
  "payrollRunId": "PR-2026-01"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The paid bonus |
| `400` | Only an approved or scheduled bonus can be marked paid. — The bonus is not approved or scheduled. |
| `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` | Bonus not found — No bonus 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/compensation/bonuses/employee/{employeeId}

**Get an employee's bonuses**

`operationId: CompensationController_getEmployeeBonuses`

One employee's bonus history.

#### Signature

```http
GET /business-made/compensation/bonuses/employee/{employeeId} (employeeId: string) -> Bonuses
```

#### Access

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

#### Errors

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

#### See also

- `GET /business-made/compensation/bonuses`

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

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Bonuses |
| `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/compensation/metrics

**Get compensation metrics**

`operationId: CompensationController_getCompensationMetrics`

Aggregate pay figures — distribution against bands, compa-ratios and where people sit outside their grade.

#### Signature

```http
GET /business-made/compensation/metrics () -> Compensation metrics
```

#### Access

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

#### Errors

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

#### See also

- `GET /business-made/compensation/grades`

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

### Responses

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

