# Academics

Course categories, courses, teaching groups, weekly schedules, generated sessions, and enrollments.

Generated from `apps/api/openapi.json`. HTML version: https://docs.institflow.com/en/reference/academics

28 operations.

## GET /api/v1/course-categories

The tenant’s course categories, nested as a tree under their parent.

- Operation id: `CourseCategoriesController_tree_v1`
- Permission: `courses.read`
- Auth: credential required
- `Idempotency-Key`: not required

### Responses

**200** — Success.

```json
[
  {
    "id": "37386ae0-3738-7738-8386-37386ae03738",
    "parentId": "06523528-0652-7652-8523-065235280652",
    "name": "Algebra I",
    "slug": "string",
    "sortOrder": -9007199254740991,
    "isActive": true,
    "createdAt": "2026-03-02T09:00:00.000Z",
    "updatedAt": "2026-03-02T09:00:00.000Z",
    "children": [
      null
    ]
  }
]
```

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X GET "https://your-institute.example/api/v1/course-categories" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## POST /api/v1/course-categories

Create a course category.

- Operation id: `CourseCategoriesController_create_v1`
- Permission: `courses.write`
- Auth: credential required
- `Idempotency-Key`: required for API-key callers

### Request body

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `parentId` | string (uuid) | no |  |
| `name` | string | yes | 1–150 chars |
| `slug` | string | yes | 1–150 chars |
| `sortOrder` | integer | no | -9007199254740991–9007199254740991 · default: 0 |
| `isActive` | boolean | no | default: true |

```json
{
  "parentId": "06523528-0652-7652-8523-065235280652",
  "name": "Algebra I",
  "slug": "string",
  "sortOrder": 0,
  "isActive": true
}
```

### Responses

**201** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes |  |
| `parentId` | string (uuid) \| null | yes |  |
| `name` | string | yes |  |
| `slug` | string | yes |  |
| `sortOrder` | integer | yes | -9007199254740991–9007199254740991 |
| `isActive` | boolean | yes |  |
| `createdAt` | string (date-time) | yes |  |
| `updatedAt` | string (date-time) | yes |  |

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X POST "https://your-institute.example/api/v1/course-categories" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "parentId": "06523528-0652-7652-8523-065235280652",
    "name": "Algebra I",
    "slug": "string",
    "sortOrder": 0,
    "isActive": true
  }'
```

## DELETE /api/v1/course-categories/{id}

Delete a course category. Fails with 409 if it has child categories or courses.

- Operation id: `CourseCategoriesController_remove_v1`
- Permission: `courses.write`
- Auth: credential required
- `Idempotency-Key`: required for API-key callers

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes |  |

### Responses

**200** — The category was deleted.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `ok` | enum: true | yes |  |

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X DELETE "https://your-institute.example/api/v1/course-categories/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## GET /api/v1/course-categories/{id}

Get one course category.

- Operation id: `CourseCategoriesController_getById_v1`
- Permission: `courses.read`
- Auth: credential required
- `Idempotency-Key`: not required

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes |  |

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes |  |
| `parentId` | string (uuid) \| null | yes |  |
| `name` | string | yes |  |
| `slug` | string | yes |  |
| `sortOrder` | integer | yes | -9007199254740991–9007199254740991 |
| `isActive` | boolean | yes |  |
| `createdAt` | string (date-time) | yes |  |
| `updatedAt` | string (date-time) | yes |  |

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X GET "https://your-institute.example/api/v1/course-categories/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## PATCH /api/v1/course-categories/{id}

Update a course category (name, parent, sort order, active flag).

- Operation id: `CourseCategoriesController_update_v1`
- Permission: `courses.write`
- Auth: credential required
- `Idempotency-Key`: required for API-key callers

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes |  |

### Request body

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `parentId` | string (uuid) \| null | no |  |
| `name` | string | no | 1–150 chars |
| `slug` | string | no | 1–150 chars |
| `sortOrder` | integer | no | -9007199254740991–9007199254740991 · default: 0 |
| `isActive` | boolean | no | default: true |

```json
{
  "parentId": "06523528-0652-7652-8523-065235280652",
  "name": "Algebra I",
  "slug": "string",
  "sortOrder": 0,
  "isActive": true
}
```

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes |  |
| `parentId` | string (uuid) \| null | yes |  |
| `name` | string | yes |  |
| `slug` | string | yes |  |
| `sortOrder` | integer | yes | -9007199254740991–9007199254740991 |
| `isActive` | boolean | yes |  |
| `createdAt` | string (date-time) | yes |  |
| `updatedAt` | string (date-time) | yes |  |

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X PATCH "https://your-institute.example/api/v1/course-categories/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "parentId": "06523528-0652-7652-8523-065235280652",
    "name": "Algebra I",
    "slug": "string",
    "sortOrder": 0,
    "isActive": true
  }'
```

## GET /api/v1/courses

List courses (cursor-paginated, filter by category/active).

- Operation id: `CoursesController_list_v1`
- Permission: `courses.read`
- Auth: credential required
- `Idempotency-Key`: not required

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `cursor` | query | string | no |  |
| `limit` | query | integer | no | 1–100 · default: 25 |
| `categoryId` | query | string (uuid) | no |  |
| `isActive` | query | boolean | no |  |

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `data` | array<object> | yes |  |
| `nextCursor` | string \| null | yes |  |

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X GET "https://your-institute.example/api/v1/courses" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## POST /api/v1/courses

Create a course.

- Operation id: `CoursesController_create_v1`
- Permission: `courses.write`
- Auth: credential required
- `Idempotency-Key`: required for API-key callers

### Request body

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `categoryId` | string (uuid) | no |  |
| `code` | string \| string | no |  |
| `name` | string | yes | 1–200 chars |
| `description` | string \| string | no |  |
| `level` | string \| string | no |  |
| `defaultFeeMinor` | integer | no | 0–9007199254740991 |
| `currency` | enum: "ILS" \| "USD" \| "EUR" | no | default: "ILS" |
| `defaultCapacity` | integer | no | 1–9007199254740991 |
| `color` | string \| string | no |  |
| `isActive` | boolean | no | default: true |
| `isPublic` | boolean | no | default: false |
| `language` | string \| string | no |  |
| `deliveryMode` | enum: "group" \| "self_paced" | no | default: "group" |

```json
{
  "categoryId": "786111a4-7861-7861-8611-786111a47861",
  "code": "string",
  "name": "Algebra I",
  "description": "A short, human-readable description.",
  "level": "string",
  "defaultFeeMinor": 0,
  "currency": "ILS",
  "defaultCapacity": 1,
  "color": "string",
  "isActive": true,
  "isPublic": false,
  "language": "string",
  "deliveryMode": "group"
}
```

### Responses

**201** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes |  |
| `categoryId` | string (uuid) \| null | yes |  |
| `code` | string \| null | yes | ≥1 chars |
| `name` | string | yes |  |
| `description` | string \| null | yes | ≥1 chars |
| `level` | string \| null | yes | ≥1 chars |
| `defaultFeeMinor` | integer \| null | yes | -9007199254740991–9007199254740991 |
| `currency` | enum: "ILS" \| "USD" \| "EUR" | yes |  |
| `defaultCapacity` | integer \| null | yes | -9007199254740991–9007199254740991 |
| `color` | string \| null | yes | ≥1 chars |
| `isActive` | boolean | yes |  |
| `isPublic` | boolean | yes |  |
| `language` | string \| null | yes | ≥1 chars |
| `deliveryMode` | enum: "group" \| "self_paced" | yes |  |
| `createdAt` | string (date-time) | yes |  |
| `updatedAt` | string (date-time) | yes |  |

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X POST "https://your-institute.example/api/v1/courses" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "categoryId": "786111a4-7861-7861-8611-786111a47861",
    "code": "string",
    "name": "Algebra I",
    "description": "A short, human-readable description.",
    "level": "string",
    "defaultFeeMinor": 0,
    "currency": "ILS",
    "defaultCapacity": 1,
    "color": "string",
    "isActive": true,
    "isPublic": false,
    "language": "string",
    "deliveryMode": "group"
  }'
```

## DELETE /api/v1/courses/{id}

Delete a course. If any group references it, it is deactivated instead of deleted.

- Operation id: `CoursesController_remove_v1`
- Permission: `courses.write`
- Auth: credential required
- `Idempotency-Key`: required for API-key callers

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes |  |

### Responses

**200** — The course was deleted or deactivated.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `ok` | enum: true | yes |  |
| `deactivated` | boolean | yes |  |

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X DELETE "https://your-institute.example/api/v1/courses/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## GET /api/v1/courses/{id}

Get one course.

- Operation id: `CoursesController_getById_v1`
- Permission: `courses.read`
- Auth: credential required
- `Idempotency-Key`: not required

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes |  |

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes |  |
| `categoryId` | string (uuid) \| null | yes |  |
| `code` | string \| null | yes | ≥1 chars |
| `name` | string | yes |  |
| `description` | string \| null | yes | ≥1 chars |
| `level` | string \| null | yes | ≥1 chars |
| `defaultFeeMinor` | integer \| null | yes | -9007199254740991–9007199254740991 |
| `currency` | enum: "ILS" \| "USD" \| "EUR" | yes |  |
| `defaultCapacity` | integer \| null | yes | -9007199254740991–9007199254740991 |
| `color` | string \| null | yes | ≥1 chars |
| `isActive` | boolean | yes |  |
| `isPublic` | boolean | yes |  |
| `language` | string \| null | yes | ≥1 chars |
| `deliveryMode` | enum: "group" \| "self_paced" | yes |  |
| `createdAt` | string (date-time) | yes |  |
| `updatedAt` | string (date-time) | yes |  |

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X GET "https://your-institute.example/api/v1/courses/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## PATCH /api/v1/courses/{id}

Update a course.

- Operation id: `CoursesController_update_v1`
- Permission: `courses.write`
- Auth: credential required
- `Idempotency-Key`: required for API-key callers

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes |  |

### Request body

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `categoryId` | string (uuid) \| null | no |  |
| `code` | enum: "" \| string \| null | no |  |
| `name` | string | no | 1–200 chars |
| `description` | enum: "" \| string \| null | no |  |
| `level` | enum: "" \| string \| null | no |  |
| `defaultFeeMinor` | integer | no | 0–9007199254740991 |
| `currency` | enum: "ILS" \| "USD" \| "EUR" | no | default: "ILS" |
| `defaultCapacity` | integer | no | 1–9007199254740991 |
| `color` | enum: "" \| string \| null | no |  |
| `isActive` | boolean | no | default: true |
| `isPublic` | boolean | no | default: false |
| `language` | enum: "" \| string \| null | no |  |
| `deliveryMode` | enum: "group" \| "self_paced" | no | default: "group" |

```json
{
  "categoryId": "786111a4-7861-7861-8611-786111a47861",
  "code": "",
  "name": "Algebra I",
  "description": "",
  "level": "",
  "defaultFeeMinor": 0,
  "currency": "ILS",
  "defaultCapacity": 1,
  "color": "",
  "isActive": true,
  "isPublic": false,
  "language": "",
  "deliveryMode": "group"
}
```

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes |  |
| `categoryId` | string (uuid) \| null | yes |  |
| `code` | string \| null | yes | ≥1 chars |
| `name` | string | yes |  |
| `description` | string \| null | yes | ≥1 chars |
| `level` | string \| null | yes | ≥1 chars |
| `defaultFeeMinor` | integer \| null | yes | -9007199254740991–9007199254740991 |
| `currency` | enum: "ILS" \| "USD" \| "EUR" | yes |  |
| `defaultCapacity` | integer \| null | yes | -9007199254740991–9007199254740991 |
| `color` | string \| null | yes | ≥1 chars |
| `isActive` | boolean | yes |  |
| `isPublic` | boolean | yes |  |
| `language` | string \| null | yes | ≥1 chars |
| `deliveryMode` | enum: "group" \| "self_paced" | yes |  |
| `createdAt` | string (date-time) | yes |  |
| `updatedAt` | string (date-time) | yes |  |

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X PATCH "https://your-institute.example/api/v1/courses/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "categoryId": "786111a4-7861-7861-8611-786111a47861",
    "code": "",
    "name": "Algebra I",
    "description": "",
    "level": "",
    "defaultFeeMinor": 0,
    "currency": "ILS",
    "defaultCapacity": 1,
    "color": "",
    "isActive": true,
    "isPublic": false,
    "language": "",
    "deliveryMode": "group"
  }'
```

## POST /api/v1/enrollments

Enroll a student into a group: capacity-checked, price snapshotted from the group/course fee, emits `enrollment.created`.

- Operation id: `EnrollmentsController_create_v1`
- Permission: `enrollments.write`
- Auth: credential required
- `Idempotency-Key`: required for API-key callers

### Request body

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `studentId` | string (uuid) | yes |  |
| `groupId` | string (uuid) | yes |  |
| `priceMinor` | integer | no | 0–9007199254740991 |
| `currency` | enum: "ILS" \| "USD" \| "EUR" | no |  |
| `discountMinor` | integer | no | 0–9007199254740991 · default: 0 |
| `discountReason` | string \| string | no |  |

```json
{
  "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
  "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
  "priceMinor": 12000,
  "currency": "ILS",
  "discountMinor": 0,
  "discountReason": "string"
}
```

### Responses

**201** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes |  |
| `studentId` | string (uuid) | yes |  |
| `groupId` | string (uuid) | yes |  |
| `status` | enum: "pending" \| "active" \| "completed" \| "dropped" | yes |  |
| `enrolledAt` | string (date-time) | yes |  |
| `endedAt` | string (date-time) \| null | yes |  |
| `priceMinor` | integer | yes | -9007199254740991–9007199254740991 |
| `currency` | enum: "ILS" \| "USD" \| "EUR" | yes |  |
| `discountMinor` | integer | yes | -9007199254740991–9007199254740991 |
| `discountReason` | string \| null | yes | ≥1 chars |
| `createdAt` | string (date-time) | yes |  |
| `updatedAt` | string (date-time) | yes |  |

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X POST "https://your-institute.example/api/v1/enrollments" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
    "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
    "priceMinor": 12000,
    "currency": "ILS",
    "discountMinor": 0,
    "discountReason": "string"
  }'
```

## POST /api/v1/enrollments/{id}/complete

Mark an enrollment as completed.

- Operation id: `EnrollmentsController_complete_v1`
- Permission: `enrollments.write`
- Auth: credential required
- `Idempotency-Key`: required for API-key callers

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes |  |

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes |  |
| `studentId` | string (uuid) | yes |  |
| `groupId` | string (uuid) | yes |  |
| `status` | enum: "pending" \| "active" \| "completed" \| "dropped" | yes |  |
| `enrolledAt` | string (date-time) | yes |  |
| `endedAt` | string (date-time) \| null | yes |  |
| `priceMinor` | integer | yes | -9007199254740991–9007199254740991 |
| `currency` | enum: "ILS" \| "USD" \| "EUR" | yes |  |
| `discountMinor` | integer | yes | -9007199254740991–9007199254740991 |
| `discountReason` | string \| null | yes | ≥1 chars |
| `createdAt` | string (date-time) | yes |  |
| `updatedAt` | string (date-time) | yes |  |

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X POST "https://your-institute.example/api/v1/enrollments/37386ae0-3738-7738-8386-37386ae03738/complete" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## POST /api/v1/enrollments/{id}/drop

Drop an enrollment. Emits `enrollment.dropped`.

- Operation id: `EnrollmentsController_drop_v1`
- Permission: `enrollments.write`
- Auth: credential required
- `Idempotency-Key`: required for API-key callers

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes |  |

### Request body

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `reason` | string | no | 0–500 chars |

```json
{
  "reason": "Requested by guardian."
}
```

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes |  |
| `studentId` | string (uuid) | yes |  |
| `groupId` | string (uuid) | yes |  |
| `status` | enum: "pending" \| "active" \| "completed" \| "dropped" | yes |  |
| `enrolledAt` | string (date-time) | yes |  |
| `endedAt` | string (date-time) \| null | yes |  |
| `priceMinor` | integer | yes | -9007199254740991–9007199254740991 |
| `currency` | enum: "ILS" \| "USD" \| "EUR" | yes |  |
| `discountMinor` | integer | yes | -9007199254740991–9007199254740991 |
| `discountReason` | string \| null | yes | ≥1 chars |
| `createdAt` | string (date-time) | yes |  |
| `updatedAt` | string (date-time) | yes |  |

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X POST "https://your-institute.example/api/v1/enrollments/37386ae0-3738-7738-8386-37386ae03738/drop" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "reason": "Requested by guardian."
  }'
```

## POST /api/v1/enrollments/{id}/transfer

Transfer an enrollment to another group: drops the source and creates a new one, in one transaction.

- Operation id: `EnrollmentsController_transfer_v1`
- Permission: `enrollments.write`
- Auth: credential required
- `Idempotency-Key`: required for API-key callers

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes |  |

### Request body

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `toGroupId` | string (uuid) | yes |  |

```json
{
  "toGroupId": "6d0ea93a-6d0e-7d0e-80ea-6d0ea93a6d0e"
}
```

### Responses

**201** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes |  |
| `studentId` | string (uuid) | yes |  |
| `groupId` | string (uuid) | yes |  |
| `status` | enum: "pending" \| "active" \| "completed" \| "dropped" | yes |  |
| `enrolledAt` | string (date-time) | yes |  |
| `endedAt` | string (date-time) \| null | yes |  |
| `priceMinor` | integer | yes | -9007199254740991–9007199254740991 |
| `currency` | enum: "ILS" \| "USD" \| "EUR" | yes |  |
| `discountMinor` | integer | yes | -9007199254740991–9007199254740991 |
| `discountReason` | string \| null | yes | ≥1 chars |
| `createdAt` | string (date-time) | yes |  |
| `updatedAt` | string (date-time) | yes |  |

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X POST "https://your-institute.example/api/v1/enrollments/37386ae0-3738-7738-8386-37386ae03738/transfer" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "toGroupId": "6d0ea93a-6d0e-7d0e-80ea-6d0ea93a6d0e"
  }'
```

## GET /api/v1/groups

List groups (cursor-paginated). A `groups.write`-less caller (e.g. a teacher) only sees groups they teach.

- Operation id: `GroupsController_list_v1`
- Permission: `groups.read`
- Auth: credential required
- `Idempotency-Key`: not required

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `cursor` | query | string | no |  |
| `limit` | query | integer | no | 1–100 · default: 25 |
| `branchId` | query | string (uuid) | no |  |
| `courseId` | query | string (uuid) | no |  |
| `status` | query | enum: "planned" \| "active" \| "completed" \| "cancelled" | no |  |
| `teacherStaffId` | query | string (uuid) | no |  |

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `data` | array<object> | yes |  |
| `nextCursor` | string \| null | yes |  |

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X GET "https://your-institute.example/api/v1/groups" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## POST /api/v1/groups

Create a group, optionally with its initial weekly schedules.

- Operation id: `GroupsController_create_v1`
- Permission: `groups.write`
- Auth: credential required
- `Idempotency-Key`: required for API-key callers

### Request body

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `branchId` | string (uuid) | yes |  |
| `courseId` | string (uuid) | yes |  |
| `name` | string | yes | 1–150 chars |
| `teacherStaffId` | string (uuid) | no |  |
| `capacity` | integer | no | 1–9007199254740991 |
| `startDate` | string (date) | yes |  |
| `endDate` | string \| string (date) | no |  |
| `status` | enum: "planned" \| "active" \| "completed" \| "cancelled" | no | default: "planned" |
| `feeMinor` | integer | no | 0–9007199254740991 |
| `room` | string \| string | no |  |
| `schedules` | array<object> | no |  |

```json
{
  "branchId": "390b8975-390b-790b-80b8-390b8975390b",
  "courseId": "d0e4743a-d0e4-70e4-8e47-d0e4743ad0e4",
  "name": "Algebra I",
  "teacherStaffId": "22f9eb14-22f9-72f9-8f9e-22f9eb1422f9",
  "capacity": 1,
  "startDate": "2026-03-02",
  "endDate": "string",
  "status": "planned",
  "feeMinor": 0,
  "room": "string",
  "schedules": [
    {
      "weekday": 0,
      "startTime": "string",
      "endTime": "string",
      "validFrom": "2026-03-02",
      "validTo": "string"
    }
  ]
}
```

### Responses

**201** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes |  |
| `branchId` | string (uuid) | yes |  |
| `courseId` | string (uuid) | yes |  |
| `name` | string | yes |  |
| `teacherStaffId` | string (uuid) \| null | yes |  |
| `capacity` | integer \| null | yes | -9007199254740991–9007199254740991 |
| `startDate` | string (date) | yes |  |
| `endDate` | string (date) \| null | yes |  |
| `status` | enum: "planned" \| "active" \| "completed" \| "cancelled" | yes |  |
| `feeMinor` | integer \| null | yes | -9007199254740991–9007199254740991 |
| `room` | string \| null | yes | ≥1 chars |
| `reminderMode` | enum: "inherit" \| "on" \| "off" | yes |  |
| `createdAt` | string (date-time) | yes |  |
| `updatedAt` | string (date-time) | yes |  |

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X POST "https://your-institute.example/api/v1/groups" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "branchId": "390b8975-390b-790b-80b8-390b8975390b",
    "courseId": "d0e4743a-d0e4-70e4-8e47-d0e4743ad0e4",
    "name": "Algebra I",
    "teacherStaffId": "22f9eb14-22f9-72f9-8f9e-22f9eb1422f9",
    "capacity": 1,
    "startDate": "2026-03-02",
    "endDate": "string",
    "status": "planned",
    "feeMinor": 0,
    "room": "string",
    "schedules": [
      {
        "weekday": 0,
        "startTime": "string",
        "endTime": "string",
        "validFrom": "2026-03-02",
        "validTo": "string"
      }
    ]
  }'
```

## DELETE /api/v1/groups/{id}

Delete a group. Fails with 409 if it has enrollments or sessions.

- Operation id: `GroupsController_remove_v1`
- Permission: `groups.write`
- Auth: credential required
- `Idempotency-Key`: required for API-key callers

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes |  |

### Responses

**200** — The group was deleted.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `ok` | enum: true | yes |  |

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X DELETE "https://your-institute.example/api/v1/groups/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## GET /api/v1/groups/{id}

Get one group.

- Operation id: `GroupsController_getById_v1`
- Permission: `groups.read`
- Auth: credential required
- `Idempotency-Key`: not required

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes |  |

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes |  |
| `branchId` | string (uuid) | yes |  |
| `courseId` | string (uuid) | yes |  |
| `name` | string | yes |  |
| `teacherStaffId` | string (uuid) \| null | yes |  |
| `capacity` | integer \| null | yes | -9007199254740991–9007199254740991 |
| `startDate` | string (date) | yes |  |
| `endDate` | string (date) \| null | yes |  |
| `status` | enum: "planned" \| "active" \| "completed" \| "cancelled" | yes |  |
| `feeMinor` | integer \| null | yes | -9007199254740991–9007199254740991 |
| `room` | string \| null | yes | ≥1 chars |
| `reminderMode` | enum: "inherit" \| "on" \| "off" | yes |  |
| `createdAt` | string (date-time) | yes |  |
| `updatedAt` | string (date-time) | yes |  |

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X GET "https://your-institute.example/api/v1/groups/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## PATCH /api/v1/groups/{id}

Update a group (including status transitions).

- Operation id: `GroupsController_update_v1`
- Permission: `groups.write`
- Auth: credential required
- `Idempotency-Key`: required for API-key callers

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes |  |

### Request body

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `branchId` | string (uuid) | no |  |
| `courseId` | string (uuid) | no |  |
| `name` | string | no | 1–150 chars |
| `teacherStaffId` | string (uuid) \| null | no |  |
| `capacity` | integer | no | 1–9007199254740991 |
| `startDate` | string (date) | no |  |
| `endDate` | string (date) \| null | no |  |
| `status` | enum: "planned" \| "active" \| "completed" \| "cancelled" | no | default: "planned" |
| `feeMinor` | integer \| null | no | 0–9007199254740991 |
| `room` | enum: "" \| string \| null | no |  |
| `reminderMode` | enum: "inherit" \| "on" \| "off" | no |  |

```json
{
  "branchId": "390b8975-390b-790b-80b8-390b8975390b",
  "courseId": "d0e4743a-d0e4-70e4-8e47-d0e4743ad0e4",
  "name": "Algebra I",
  "teacherStaffId": "22f9eb14-22f9-72f9-8f9e-22f9eb1422f9",
  "capacity": 1,
  "startDate": "2026-03-02",
  "endDate": "2026-03-02",
  "status": "planned",
  "feeMinor": 0,
  "room": "",
  "reminderMode": "inherit"
}
```

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes |  |
| `branchId` | string (uuid) | yes |  |
| `courseId` | string (uuid) | yes |  |
| `name` | string | yes |  |
| `teacherStaffId` | string (uuid) \| null | yes |  |
| `capacity` | integer \| null | yes | -9007199254740991–9007199254740991 |
| `startDate` | string (date) | yes |  |
| `endDate` | string (date) \| null | yes |  |
| `status` | enum: "planned" \| "active" \| "completed" \| "cancelled" | yes |  |
| `feeMinor` | integer \| null | yes | -9007199254740991–9007199254740991 |
| `room` | string \| null | yes | ≥1 chars |
| `reminderMode` | enum: "inherit" \| "on" \| "off" | yes |  |
| `createdAt` | string (date-time) | yes |  |
| `updatedAt` | string (date-time) | yes |  |

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X PATCH "https://your-institute.example/api/v1/groups/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "branchId": "390b8975-390b-790b-80b8-390b8975390b",
    "courseId": "d0e4743a-d0e4-70e4-8e47-d0e4743ad0e4",
    "name": "Algebra I",
    "teacherStaffId": "22f9eb14-22f9-72f9-8f9e-22f9eb1422f9",
    "capacity": 1,
    "startDate": "2026-03-02",
    "endDate": "2026-03-02",
    "status": "planned",
    "feeMinor": 0,
    "room": "",
    "reminderMode": "inherit"
  }'
```

## GET /api/v1/groups/{id}/enrollments

List a group’s enrollments (cursor-paginated).

- Operation id: `GroupsController_listEnrollments_v1`
- Permission: `enrollments.read`
- Auth: credential required
- `Idempotency-Key`: not required

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes |  |
| `cursor` | query | string | no |  |
| `limit` | query | integer | no | 1–100 · default: 25 |

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `data` | array<object> | yes |  |
| `nextCursor` | string \| null | yes |  |

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X GET "https://your-institute.example/api/v1/groups/37386ae0-3738-7738-8386-37386ae03738/enrollments" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## GET /api/v1/groups/{id}/schedules

List a group’s weekly schedule rules.

- Operation id: `GroupsController_listSchedules_v1`
- Permission: `groups.read`
- Auth: credential required
- `Idempotency-Key`: not required

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes |  |

### Responses

**200** — Success.

```json
[
  {
    "id": "37386ae0-3738-7738-8386-37386ae03738",
    "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
    "weekday": 0,
    "startTime": "string",
    "endTime": "string",
    "validFrom": "2026-03-02",
    "validTo": "2026-03-02"
  }
]
```

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X GET "https://your-institute.example/api/v1/groups/37386ae0-3738-7738-8386-37386ae03738/schedules" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## PUT /api/v1/groups/{id}/schedules

Replace a group’s weekly schedule rules and regenerate its upcoming sessions from them.

- Operation id: `GroupsController_replaceSchedules_v1`
- Permission: `groups.write`
- Auth: credential required
- `Idempotency-Key`: required for API-key callers

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes |  |

### Request body

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `schedules` | array<object> | yes |  |

```json
{
  "schedules": [
    {
      "weekday": 0,
      "startTime": "string",
      "endTime": "string",
      "validFrom": "2026-03-02",
      "validTo": "string"
    }
  ]
}
```

### Responses

**200** — Success.

```json
[
  {
    "id": "37386ae0-3738-7738-8386-37386ae03738",
    "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
    "weekday": 0,
    "startTime": "string",
    "endTime": "string",
    "validFrom": "2026-03-02",
    "validTo": "2026-03-02"
  }
]
```

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X PUT "https://your-institute.example/api/v1/groups/37386ae0-3738-7738-8386-37386ae03738/schedules" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "schedules": [
      {
        "weekday": 0,
        "startTime": "string",
        "endTime": "string",
        "validFrom": "2026-03-02",
        "validTo": "string"
      }
    ]
  }'
```

## GET /api/v1/groups/{id}/sessions

List a group’s sessions (cursor-paginated, optional date range and status filter — `?status=scheduled` or repeated `status=` for several).

- Operation id: `GroupsController_listSessions_v1`
- Permission: `groups.read`
- Auth: credential required
- `Idempotency-Key`: not required

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes |  |
| `cursor` | query | string | no |  |
| `limit` | query | integer | no | 1–100 · default: 25 |
| `from` | query | string (date-time) | no |  |
| `to` | query | string (date-time) | no |  |
| `status` | query | enum: "scheduled" \| "held" \| "cancelled" \| array<enum: "scheduled" \| "held" \| "cancelled"> | no |  |

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `data` | array<object> | yes |  |
| `nextCursor` | string \| null | yes |  |

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X GET "https://your-institute.example/api/v1/groups/37386ae0-3738-7738-8386-37386ae03738/sessions" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## POST /api/v1/groups/{id}/sessions/generate

Materialize this group’s sessions from its schedule rules for the rolling window.

- Operation id: `GroupsController_generateSessions_v1`
- Permission: `groups.write`
- Auth: credential required
- `Idempotency-Key`: required for API-key callers

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes |  |

### Request body

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `until` | string (date) | no |  |

```json
{
  "until": "2026-03-02"
}
```

### Responses

**201** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `created` | integer | yes | 0–9007199254740991 |
| `total` | integer | yes | 0–9007199254740991 |

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X POST "https://your-institute.example/api/v1/groups/37386ae0-3738-7738-8386-37386ae03738/sessions/generate" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "until": "2026-03-02"
  }'
```

## GET /api/v1/me/schedule

The caller’s own weekly schedule: sessions of groups they teach, are enrolled in, or their children are enrolled in.

- Operation id: `MeScheduleController_schedule_v1`
- Permission: —
- Auth: credential required
- `Idempotency-Key`: not required

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `from` | query | string (date) | yes |  |
| `to` | query | string (date) | yes |  |

### Responses

**200** — Success.

```json
[
  {
    "sessionId": "736a51cd-736a-736a-86a5-736a51cd736a",
    "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
    "groupName": "string",
    "courseId": "d0e4743a-d0e4-70e4-8e47-d0e4743ad0e4",
    "courseName": "string",
    "scheduledStart": "2026-03-02T09:00:00.000Z",
    "scheduledEnd": "2026-03-02T09:00:00.000Z",
    "status": "scheduled",
    "relation": "teacher",
    "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7"
  }
]
```

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X GET "https://your-institute.example/api/v1/me/schedule?from=2026-03-02&to=2026-03-02" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## GET /api/v1/me/teaching/today

Today’s sessions for the calling teacher, each with roster size, marked count and attendance state, plus a backlog of past held sessions with no attendance recorded.

- Operation id: `MeTeachingController_today_v1`
- Permission: —
- Auth: credential required
- `Idempotency-Key`: not required

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `date` | string (date) | yes |  |
| `timezone` | string | yes |  |
| `staffId` | string (uuid) \| null | yes |  |
| `sessions` | array<object> | yes |  |
| `unmarkedBacklog` | array<object> | yes |  |

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X GET "https://your-institute.example/api/v1/me/teaching/today" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## PATCH /api/v1/sessions/{id}

Update a session: cancel it (with a reason, emits `session.cancelled`), or set its topic/notes/substitute teacher.

- Operation id: `SessionsController_update_v1`
- Permission: `groups.write`
- Auth: credential required
- `Idempotency-Key`: required for API-key callers

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes |  |

### Request body

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `status` | enum: "cancelled" | no |  |
| `reason` | string | no | 0–500 chars |
| `topic` | enum: "" \| string \| null | no |  |
| `notes` | enum: "" \| string \| null | no |  |
| `teacherStaffId` | string (uuid) \| null | no |  |
| `lessonId` | string (uuid) \| null | no |  |

```json
{
  "status": "cancelled",
  "reason": "Requested by guardian.",
  "topic": "",
  "notes": "",
  "teacherStaffId": "22f9eb14-22f9-72f9-8f9e-22f9eb1422f9",
  "lessonId": "3d477cf4-3d47-7d47-8477-3d477cf43d47"
}
```

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes |  |
| `groupId` | string (uuid) | yes |  |
| `scheduledStart` | string (date-time) | yes |  |
| `scheduledEnd` | string (date-time) | yes |  |
| `status` | enum: "scheduled" \| "held" \| "cancelled" | yes |  |
| `teacherStaffId` | string (uuid) \| null | yes |  |
| `topic` | string \| null | yes | ≥1 chars |
| `notes` | string \| null | yes | ≥1 chars |
| `lessonId` | string (uuid) \| null | yes |  |
| `createdAt` | string (date-time) | yes |  |
| `updatedAt` | string (date-time) | yes |  |

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X PATCH "https://your-institute.example/api/v1/sessions/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "status": "cancelled",
    "reason": "Requested by guardian.",
    "topic": "",
    "notes": "",
    "teacherStaffId": "22f9eb14-22f9-72f9-8f9e-22f9eb1422f9",
    "lessonId": "3d477cf4-3d47-7d47-8477-3d477cf43d47"
  }'
```

## GET /api/v1/students/{id}/enrollments

List a student’s enrollments (cursor-paginated).

- Operation id: `StudentEnrollmentsController_list_v1`
- Permission: `enrollments.read`
- Auth: credential required
- `Idempotency-Key`: not required

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `id` | path | string | yes |  |
| `cursor` | query | string | no |  |
| `limit` | query | integer | no | 1–100 · default: 25 |

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `data` | array<object> | yes |  |
| `nextCursor` | string \| null | yes |  |

Every other status answers with the shared `ErrorEnvelope` — see
https://docs.institflow.com/md/en/error-codes.md

### Example request

```bash
curl -X GET "https://your-institute.example/api/v1/students/37386ae0-3738-7738-8386-37386ae03738/enrollments" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```
