# Business Made · Readiness gates

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

**Who can work a slot**

`operationId: ScheduleController_getAssignable`

#### Signature

```http
GET /business-made/schedules/assignable ()
```

#### Access

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

#### Notes

- Each person in `available[]` / `onLeave[]` carries `readiness: { effect: none|warn|override|block, badge: blocked|overdue|due_soon|null, reasons[], overrideActive }` for this slot. Pass `station` to judge station rules. Nobody is removed; people who qualify sort first. `readinessBlocked` counts the blocked.
- 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`.

### 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. |
| `date` | query | string | yes |  |
| `startTime` | query | string | yes |  |
| `endTime` | query | string | yes |  |
| `excludeShiftId` | query | string | yes |  |
| `businessLocationId` | query | string | yes |  |
| `station` | query | string | yes |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` |  |
| `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/schedules/week

**The week rota**

`operationId: ScheduleController_getWeek`

#### Signature

```http
GET /business-made/schedules/week ()
```

#### Access

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

#### Notes

- Readiness badges: every assigned shift carries `readiness` (null when no rule applies) = `{ employeeId, effect, badge: blocked|overdue|due_soon, blocked, reasons[], overrideActive }` and `readinessOverride` when one was recorded. Rows / `byPerson` carry `readinessBadge` (worst) and `readinessBlocked`; `totals.readiness` = `{ blocked, overdue, dueSoon }`.
- 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`.

### 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. |
| `weekStart` | query | string | yes |  |
| `businessLocationId` | query | string | yes |  |
| `lengthDays` | query | string | yes |  |
| `q` | query | string | yes |  |

### Responses

| Status | Meaning |
| --- | --- |
| `200` |  |
| `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/schedules/week/publish

**Publish a period**

`operationId: ScheduleController_publishWeek`

#### Signature

```http
POST /business-made/schedules/week/publish (body)
```

#### Access

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

#### Notes

- Scheduler gate over every assigned shift in the period. Any block without an active override stops the whole publish with 409 `readiness_block`; `blocking[]` lists each shift `{ shiftId, scheduleId, date, startTime, endTime, station, employeeId, employeeName, issues[] }`. Resend with `override` to record one override per blocked shift. The result carries `readiness: { overridden[], warnings[] }`.
- 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

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `409` | READINESS_BLOCK | <name> can't be scheduled: <requirement titles>. | A requirement rule whose `enforcement.scheduler` effect is block (or override-with-reason) is unmet for this person, and no override is active. | Show `message` and `reasons`. A location manager or HR can resend the same request with `override: { reasonCode, reason, expiresAt }` — it is written to the readiness ledger with the approver and expiry. The person’s own override is refused. |
| `400` | OVERRIDE_INVALID | An override needs reasonCode, reason, expiresAt. | `override` is missing a field, has an unknown reason code, or an expiry in the past. | — |
| `403` | OVERRIDE_FORBIDDEN | Sign in as a location manager or HR to override. | The caller may not approve an override (no caller, the person themselves, or only their own supervisor). | — |

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

### Parameters

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

### Request body

### Responses

| Status | Meaning |
| --- | --- |
| `201` |  |
| `400` | An override needs reasonCode, reason, expiresAt. — `override` is missing a field, has an unknown reason code, or an expiry in the past. |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | Sign in as a location manager or HR to override. — The caller may not approve an override (no caller, the person themselves, or only their own supervisor). |
| `409` | <name> can't be scheduled: <requirement titles>. — A requirement rule whose `enforcement.scheduler` effect is block (or override-with-reason) is unmet for this person, and no override is active. |
| `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/schedules/{scheduleId}/shifts/{shiftId}/claim

**Claim an open shift**

`operationId: ScheduleController_claimShift`

#### Signature

```http
POST /business-made/schedules/{scheduleId}/shifts/{shiftId}/claim ()
```

#### Access

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

#### Notes

- Scheduler gate: a person can’t take an open shift a rule blocks them from (409 `readiness_block`). They can’t override their own block; a manager assigns them with an override instead.
- 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

| Status | Code | Message | When | What to do |
| --- | --- | --- | --- | --- |
| `409` | READINESS_BLOCK | <name> can't take this shift: <requirement titles>. | A requirement rule whose `enforcement.scheduler` effect is block (or override-with-reason) is unmet for this person, and no override is active. | Show `message` and `reasons`. A location manager or HR can resend the same request with `override: { reasonCode, reason, expiresAt }` — it is written to the readiness ledger with the approver and expiry. The person’s own override is refused. |

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. |
| `scheduleId` | path | string | yes |  |
| `shiftId` | path | string | yes |  |

### Responses

| Status | Meaning |
| --- | --- |
| `201` |  |
| `401` | Authentication failed: Invalid or expired token — The `Authorization` header is missing, malformed, or the JWT has expired. |
| `403` | You do not have permission to perform this action — The caller is authenticated but lacks the role required by the endpoint, or is acting on another org. |
| `409` | <name> can't take this shift: <requirement titles>. — A requirement rule whose `enforcement.scheduler` effect is block (or override-with-reason) is unmet for this person, and no override is active. |
| `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. |

