# Business Made · Payroll config

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

**List earning types**

`operationId: PayrollConfigController_listEarningTypes`

The kinds of pay the org recognises — salary, hourly, overtime, bonus, commission. What a pay stub line can be.

#### Signature

```http
GET /business-made/payroll/earning-types () -> Earning types
```

#### Access

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

#### Errors

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

#### See also

- `POST /business-made/payroll/earning-types/seed`

### 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` | Earning 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/payroll/earning-types

**Create an earning type**

`operationId: PayrollConfigController_createEarningType`

Defines a kind of pay. Its tax treatment determines how the amount is taxed, so an incorrectly configured type misstates withholding on every stub that uses it.

#### Signature

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

#### Access

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

#### Errors

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

#### See also

- `POST /business-made/payroll/earning-types/seed`

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

```json
{
  "code": "OT",
  "name": "Overtime",
  "taxable": true,
  "multiplier": 1.5
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The created earning 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/payroll/earning-types/{id}

**Get an earning type**

`operationId: PayrollConfigController_getEarningType`

Fetches one earning type with its tax treatment.

#### Signature

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

#### Access

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

#### Errors

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

#### See also

- `PATCH /business-made/payroll/earning-types/{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 earning 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/payroll/earning-types/{id}

**Delete an earning type**

`operationId: PayrollConfigController_deleteEarningType`

Deletes an earning type. Historical stubs referencing it keep their figures but lose the definition behind them.

#### Signature

```http
DELETE /business-made/payroll/earning-types/{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/payroll/earning-types`

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

## PATCH /business-made/payroll/earning-types/{id}

**Update an earning type**

`operationId: PayrollConfigController_updateEarningType`

Updates an earning type. Applies to future calculations; stubs already produced keep their original treatment.

#### Signature

```http
PATCH /business-made/payroll/earning-types/{id} (id: string, body) -> The updated earning type
```

#### Access

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

#### Errors

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

#### See also

- `GET /business-made/payroll/earning-types`

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

Fields to change.

```json
{
  "multiplier": 2
}
```

### Responses

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

## POST /business-made/payroll/earning-types/seed

**Seed standard earning types**

`operationId: PayrollConfigController_seedEarningTypes`

Creates the standard set of earning types for a new org — the fast path to a working payroll setup. Idempotent, so it is safe to re-run.

#### Signature

```http
POST /business-made/payroll/earning-types/seed () -> The seeded earning types
```

#### Access

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

#### Errors

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

#### See also

- `POST /business-made/payroll/deduction-types/seed`

### 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` | The seeded earning 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/payroll/deduction-types

**List deduction types**

`operationId: PayrollConfigController_listDeductionTypes`

The kinds of deduction the org recognises — pension, health premium, garnishment, loan repayment.

#### Signature

```http
GET /business-made/payroll/deduction-types () -> Deduction types
```

#### Access

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

#### Errors

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

#### See also

- `GET /business-made/payroll/employee-deductions`

### 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` | Deduction 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/payroll/deduction-types

**Create a deduction type**

`operationId: PayrollConfigController_createDeductionType`

Defines a kind of deduction. **Whether it is pre-tax changes the taxable base**, so this setting affects tax withheld on every stub it appears on — not just the deduction line.

#### Signature

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

#### Access

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

#### Notes

- `preTax` is the field most likely to be wrong and hardest to spot afterwards.

#### Errors

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

#### See also

- `POST /business-made/payroll/deduction-types/seed`

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

```json
{
  "code": "PENSION",
  "name": "Pension contribution",
  "preTax": true,
  "annualLimit": 23000
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The created deduction 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/payroll/deduction-types/{id}

**Get a deduction type**

`operationId: PayrollConfigController_getDeductionType`

Fetches one deduction type with its pre-tax treatment and limits.

#### Signature

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

#### Access

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

#### Errors

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

#### See also

- `PATCH /business-made/payroll/deduction-types/{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 deduction 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/payroll/deduction-types/{id}

**Delete a deduction type**

`operationId: PayrollConfigController_deleteDeductionType`

Deletes a deduction type. Employee deductions referencing it are not removed — cancel those first.

#### Signature

```http
DELETE /business-made/payroll/deduction-types/{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/payroll/employee-deductions/{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. |

## PATCH /business-made/payroll/deduction-types/{id}

**Update a deduction type**

`operationId: PayrollConfigController_updateDeductionType`

Updates a deduction type. Changing `preTax` alters withholding on future runs — reconcile the year-to-date figures afterwards.

#### Signature

```http
PATCH /business-made/payroll/deduction-types/{id} (id: string, body) -> The updated deduction type
```

#### Access

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

#### Errors

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

#### See also

- `GET /business-made/payroll/deduction-types`

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

Fields to change.

```json
{
  "annualLimit": 24000
}
```

### Responses

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

## POST /business-made/payroll/deduction-types/seed

**Seed standard deduction types**

`operationId: PayrollConfigController_seedDeductionTypes`

Creates the standard deduction types for a new org. Idempotent.

#### Signature

```http
POST /business-made/payroll/deduction-types/seed () -> The seeded deduction types
```

#### Access

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

#### Errors

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

#### See also

- `POST /business-made/payroll/earning-types/seed`

### 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` | The seeded deduction 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/payroll/employee-deductions

**List employee deductions**

`operationId: PayrollConfigController_listEmployeeDeductions`

Deductions assigned to employees across the org, with their status.

#### Signature

```http
GET /business-made/payroll/employee-deductions (employeeId?: string) -> Employee deductions
```

#### Access

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

#### Errors

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

#### See also

- `POST /business-made/payroll/employee-deductions`

### 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` | query | string | — |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | Employee deductions |
| `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/payroll/employee-deductions

**Assign a deduction to an employee**

`operationId: PayrollConfigController_createEmployeeDeduction`

Assigns a recurring deduction. It applies from the next calculated run — assigning mid-period does not retrospectively deduct from a run already calculated.

#### Signature

```http
POST /business-made/payroll/employee-deductions (body) -> The assigned deduction
```

#### Access

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

#### Errors

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

#### See also

- `POST /business-made/payroll/employee-deductions/{id}/pause`

### 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 deduction to assign.

```json
{
  "employeeId": "EMP-4821",
  "deductionTypeId": "DT-pension",
  "amount": 250,
  "startDate": "2026-10-01"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The assigned deduction |
| `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/payroll/employee-deductions/{id}

**Get an employee deduction**

`operationId: PayrollConfigController_getEmployeeDeduction`

Fetches one assigned deduction with its schedule and remaining balance.

#### Signature

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

#### Access

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

#### Errors

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

#### See also

- `PATCH /business-made/payroll/employee-deductions/{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 deduction |
| `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/payroll/employee-deductions/{id}

**Delete an employee deduction**

`operationId: PayrollConfigController_deleteEmployeeDeduction`

Deletes the assignment outright. Cancel instead where the deduction history matters — for a garnishment it almost always does.

#### Signature

```http
DELETE /business-made/payroll/employee-deductions/{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/payroll/employee-deductions/{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. |

## PATCH /business-made/payroll/employee-deductions/{id}

**Update an employee deduction**

`operationId: PayrollConfigController_updateEmployeeDeduction`

Updates an assigned deduction. Takes effect on the next calculation.

#### Signature

```http
PATCH /business-made/payroll/employee-deductions/{id} (id: string, body) -> The updated deduction
```

#### Access

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

#### Errors

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

#### See also

- `POST /business-made/payroll/employee-deductions/{id}/pause`

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

Fields to change.

```json
{
  "amount": 300
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The updated deduction |
| `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/payroll/employee-deductions/{id}/pause

**Pause a deduction**

`operationId: PayrollConfigController_pauseEmployeeDeduction`

Suspends a deduction without cancelling it — a contribution holiday, or a garnishment stayed pending review. It stops applying but keeps its history and remaining balance.

#### Signature

```http
POST /business-made/payroll/employee-deductions/{id}/pause (id: string) -> The paused deduction
```

#### Access

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

#### Errors

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

#### See also

- `POST /business-made/payroll/employee-deductions/{id}/resume`

### 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 paused deduction |
| `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/payroll/employee-deductions/{id}/resume

**Resume a deduction**

`operationId: PayrollConfigController_resumeEmployeeDeduction`

Restarts a paused deduction from the next run. Missed periods are not caught up automatically.

#### Signature

```http
POST /business-made/payroll/employee-deductions/{id}/resume (id: string) -> The resumed deduction
```

#### Access

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

#### Notes

- No back-catch. Adjust manually if the missed amounts are owed.

#### Errors

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

#### See also

- `POST /business-made/payroll/employee-deductions/{id}/pause`

### 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 resumed deduction |
| `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/payroll/employee-deductions/{id}/cancel

**Cancel a deduction**

`operationId: PayrollConfigController_cancelEmployeeDeduction`

Ends a deduction permanently, keeping the record of what was deducted. The correct way to stop a loan repayment that has been settled.

#### Signature

```http
POST /business-made/payroll/employee-deductions/{id}/cancel (id: string) -> The cancelled deduction
```

#### Access

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

#### Errors

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

#### See also

- `DELETE /business-made/payroll/employee-deductions/{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 |
| --- | --- |
| `201` | The cancelled deduction |
| `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/payroll/run/preview

**Preview a payroll run**

`operationId: PayrollConfigController_previewRun`

Computes pay stubs **without persisting anything** — no stubs written, no loan balances touched, no year-to-date figures updated.

This is the dry run. Use it to check figures before `execute`, which is not reversible.

#### Signature

```http
POST /business-made/payroll/run/preview (body) -> The computed stubs, unsaved
```

#### Access

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

#### Notes

- Writes nothing. Safe to run as often as you like.
- Pay readiness: `payReadiness: { exceptions[], held[] }`. Exceptions = gov-form items (missing W-4, I-9 §2 late, re-verification, no pay method, unsigned policy) + requirement rules whose payroll gate is warn/block — each `{ employeeId, name, source: gov_forms|requirement, code, message, fix: { route, label }, effect: warn|block, requirementId?, override? }`. A block holds that employee’s stub only (`held: true`, `holdReasons[]`, a "Pay held:" warning); totals exclude held stubs and `totals.held` counts them. The run is never held silently.
- Workforce Readiness: with no requirement rules this endpoint behaves exactly as before. `warn` never stops anything — it comes back as `readiness.warnings` in the response.

#### Errors

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

#### See also

- `POST /business-made/payroll/run/execute`

### 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 run to preview.

```json
{
  "payPeriodStart": "2026-09-01",
  "payPeriodEnd": "2026-09-15",
  "employeeIds": [
    "EMP-4821"
  ]
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The computed stubs, unsaved |
| `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/payroll/run/execute

**Execute a payroll run**

`operationId: PayrollConfigController_executeRun`

Runs payroll for real: **persists pay stubs, decrements loan balances and updates year-to-date figures**.

All three side effects matter. Loan balances moving means a repeated execution over-collects; YTD figures moving means the year-end forms shift. Preview first, and do not retry a request whose response was lost without checking what was written.

#### Signature

```http
POST /business-made/payroll/run/execute (body) -> The executed run and its stubs
```

#### Access

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

#### Notes

- Not idempotent, and its side effects are cumulative — a duplicate execution double-decrements loans and double-counts YTD.
- Preview the same input first and compare.
- Pay readiness is re-checked at persist time: held stubs are not written or paid, the employee is notified (`payroll.held`) and the run records `payReadiness`. Lift a requirement block with `POST business-made/readiness/overrides` (gate payroll) or by fixing it, then pay off-cycle.
- Workforce Readiness: with no requirement rules this endpoint behaves exactly as before. `warn` never stops anything — it comes back as `readiness.warnings` in the response.

#### Errors

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

#### See also

- `POST /business-made/payroll/run/preview`

### 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 run to execute.

```json
{
  "payPeriodStart": "2026-09-01",
  "payPeriodEnd": "2026-09-15"
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The executed run and its stubs |
| `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/payroll/employees/bulk-import

**Bulk import employees with payroll profiles**

`operationId: PayrollConfigController_bulkImport`

Imports employees together with their payroll profiles in one operation — the migration path when moving from another provider.

Check a small batch before importing a whole workforce: pay rates and tax elections arriving wrong here become wrong payslips.

#### Signature

```http
POST /business-made/payroll/employees/bulk-import (body) -> The import result
```

#### Access

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

#### Notes

- Not idempotent — re-running an import can duplicate employees.

#### Errors

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

#### See also

- `POST /business-made/payroll/run/preview`

### 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 rows to import.

```json
{
  "rows": [
    {
      "employeeId": "E-00412",
      "firstName": "Ada",
      "lastName": "Lovelace",
      "annualSalary": 76800,
      "payFrequency": "semi-monthly"
    }
  ]
}
```

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The import 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/payroll/runs/{runId}/ach/generate

**Generate the ACH file for a run**

`operationId: PayrollConfigController_generateAch`

Produces the NACHA file that instructs the bank to pay everyone in the run.

**This file moves money once submitted to your bank.** Generating it here does not transmit it — but anything downstream that uploads it does, and a file generated twice and submitted twice pays twice.

#### Signature

```http
POST /business-made/payroll/runs/{runId}/ach/generate (runId: string) -> The generated ACH file reference
```

#### Access

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

#### Notes

- Generation is not transmission — but treat the file as live payment instructions from the moment it exists.

#### Errors

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

#### See also

- `GET /business-made/payroll/runs/{runId}/ach/download`

### 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. |
| `runId` | path | string | yes | Payroll run id. |

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The generated ACH file reference |
| `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/payroll/runs/{runId}/ach/download

**Download the ACH file**

`operationId: PayrollConfigController_downloadAch`

Downloads the NACHA file for a run, for submission to your bank. Handle it as a payment instruction: it contains every employee's bank details and the amounts owed.

#### Signature

```http
GET /business-made/payroll/runs/{runId}/ach/download (runId: string) -> The NACHA file
```

#### Access

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

#### Notes

- Contains full bank details for every employee in the run.

#### Errors

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

#### See also

- `POST /business-made/payroll/runs/{runId}/ach/generate`

### 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. |
| `runId` | path | string | yes | Payroll run id. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The NACHA file |
| `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/payroll/stubs/{stubId}/pdf/generate

**Generate a pay stub PDF**

`operationId: PayrollConfigController_generateStubPdf`

Generates the PDF for a pay stub, or returns the cached one. Pass `regenerate` to force a fresh render after correcting the underlying stub.

#### Signature

```http
POST /business-made/payroll/stubs/{stubId}/pdf/generate (stubId: string, regenerate?: string) -> The generated PDF reference
```

#### Access

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

#### Errors

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

#### See also

- `GET /business-made/payroll/stubs/{stubId}/pdf`

### 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. |
| `stubId` | path | string | yes | Pay stub id. |
| `regenerate` | query | string | — | Force a fresh generation instead of returning the cached document. Send `true`. |

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The generated PDF reference |
| `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/payroll/stubs/{stubId}/pdf

**Download a pay stub PDF**

`operationId: PayrollConfigController_downloadStubPdf`

Downloads a pay stub as a PDF. Contains pay, deductions and year-to-date figures — restrict it to the employee it belongs to.

#### Signature

```http
GET /business-made/payroll/stubs/{stubId}/pdf (stubId: string, regenerate?: string) -> The pay stub PDF
```

#### Access

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

#### Errors

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

#### See also

- `POST /business-made/payroll/stubs/{stubId}/pdf/generate`

### 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. |
| `stubId` | path | string | yes | Pay stub id. |
| `regenerate` | query | string | — | Force a fresh generation instead of returning the cached document. Send `true`. |

### Responses

| Status | Meaning |
| --- | --- |
| `200` | The pay stub PDF |
| `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/payroll/runs/{runId}/stubs/pdf/generate

**Generate all pay stub PDFs for a run**

`operationId: PayrollConfigController_generateStubPdfsForRun`

Renders every pay stub in a run in one operation — the distribution step after processing.

#### Signature

```http
POST /business-made/payroll/runs/{runId}/stubs/pdf/generate (runId: string) -> The generation result
```

#### Access

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

#### Errors

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

#### See also

- `GET /business-made/payroll/stubs/run/{payrollRunId}`

### 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. |
| `runId` | path | string | yes | Payroll run id. |

### Responses

| Status | Meaning |
| --- | --- |
| `201` | The generation 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. |

