# People

Students, guardians, staff, and the authenticated caller’s own profile.

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

43 operations.

## GET /api/v1/guardians

List guardians (cursor-paginated). `q` searches name/phone.

- Operation id: `GuardiansController_list_v1`
- Permission: `guardians.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 |
| `q` | query | string | no | 1–200 chars |

### 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/guardians" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## POST /api/v1/guardians

Create a guardian. `phone` must be unique within the tenant (409 otherwise).

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

### Request body

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `userId` | string (uuid) | no |  |
| `firstName` | string | yes | 1–100 chars |
| `lastName` | string | yes | 1–100 chars |
| `phone` | string | yes |  |
| `email` | enum: "" \| string (email) | no |  |
| `nationalId` | enum: "" \| string | no |  |
| `preferredLocale` | enum: "ar" \| "en" \| "he" | no | default: "ar" |
| `address` | object | no |  |

```json
{
  "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
  "firstName": "Ada",
  "lastName": "Lovelace",
  "phone": "+15551234567",
  "email": "",
  "nationalId": "",
  "preferredLocale": "ar",
  "address": {}
}
```

### Responses

**201** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes |  |
| `firstName` | string | yes |  |
| `lastName` | string | yes |  |
| `phone` | string | yes |  |
| `email` | string (email) \| null | yes |  |
| `nationalId` | string \| null | yes | ≥1 chars |
| `preferredLocale` | enum: "ar" \| "en" \| "he" | yes |  |
| `address` | object \| null | yes |  |
| `userId` | 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 POST "https://your-institute.example/api/v1/guardians" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
    "firstName": "Ada",
    "lastName": "Lovelace",
    "phone": "+15551234567",
    "email": "",
    "nationalId": "",
    "preferredLocale": "ar",
    "address": {}
  }'
```

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

Soft-delete a guardian.

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

### Parameters

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

### Responses

**200** — The guardian was deleted.

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/guardians/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

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

Get one guardian by id.

- Operation id: `GuardiansController_getById_v1`
- Permission: `guardians.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 |  |
| `firstName` | string | yes |  |
| `lastName` | string | yes |  |
| `phone` | string | yes |  |
| `email` | string (email) \| null | yes |  |
| `nationalId` | string \| null | yes | ≥1 chars |
| `preferredLocale` | enum: "ar" \| "en" \| "he" | yes |  |
| `address` | object \| null | yes |  |
| `userId` | 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 GET "https://your-institute.example/api/v1/guardians/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

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

Update a guardian.

- Operation id: `GuardiansController_update_v1`
- Permission: `guardians.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 |
| --- | --- | --- | --- |
| `userId` | string (uuid) | no |  |
| `firstName` | string | no | 1–100 chars |
| `lastName` | string | no | 1–100 chars |
| `phone` | string | no |  |
| `email` | enum: "" \| string (email) | no |  |
| `nationalId` | enum: "" \| string | no |  |
| `preferredLocale` | enum: "ar" \| "en" \| "he" | no | default: "ar" |
| `address` | object | no |  |

```json
{
  "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
  "firstName": "Ada",
  "lastName": "Lovelace",
  "phone": "+15551234567",
  "email": "",
  "nationalId": "",
  "preferredLocale": "ar",
  "address": {}
}
```

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes |  |
| `firstName` | string | yes |  |
| `lastName` | string | yes |  |
| `phone` | string | yes |  |
| `email` | string (email) \| null | yes |  |
| `nationalId` | string \| null | yes | ≥1 chars |
| `preferredLocale` | enum: "ar" \| "en" \| "he" | yes |  |
| `address` | object \| null | yes |  |
| `userId` | 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/guardians/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
    "firstName": "Ada",
    "lastName": "Lovelace",
    "phone": "+15551234567",
    "email": "",
    "nationalId": "",
    "preferredLocale": "ar",
    "address": {}
  }'
```

## GET /api/v1/guardians/{id}/family-summary

The family account view: each child with their active groups and 30-day attendance rate, plus — for a caller holding `invoices.read` — per-child balances, the consolidated family balance and the open invoices this guardian is the payer on. Every money field is `null` without `invoices.read`.

- Operation id: `GuardiansController_getFamilySummary_v1`
- Permission: `guardians.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 |
| --- | --- | --- | --- |
| `guardianId` | string (uuid) | yes |  |
| `children` | array<FamilyChild> | yes |  |
| `financial` | object \| 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/guardians/37386ae0-3738-7738-8386-37386ae03738/family-summary" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## GET /api/v1/guardians/{id}/portal-invite

The newest still-open portal invitation for this guardian and whether its email has left (`deliveryState`: queued, sent or failed, with a `deliveryReason`), or `invitation: null`.

- Operation id: `GuardiansController_portalInviteStatus_v1`
- Permission: `guardians.write`
- Auth: credential required
- `Idempotency-Key`: not required

### Parameters

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

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `invitation` | object \| 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/guardians/37386ae0-3738-7738-8386-37386ae03738/portal-invite" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## POST /api/v1/guardians/{id}/portal-invite

Invite this guardian to the portal: creates (or reuses) their login and links it to this record. Defaults to the guardian’s own email address.

- Operation id: `GuardiansController_portalInvite_v1`
- Permission: `guardians.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 |
| --- | --- | --- | --- |
| `email` | string \| string (email) | no |  |
| `username` | string \| string | no |  |

```json
{
  "email": "ada.lovelace@example.com",
  "username": "string"
}
```

### Responses

**201** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `status` | enum: "invited" | yes |  |
| `email` | string (email) \| null | yes |  |
| `username` | string \| null | yes |  |
| `invitationId` | string (uuid) | yes |  |
| `expiresAt` | string (date-time) | yes |  |
| `deliveryState` | enum: "queued" \| "sent" \| "failed" | yes |  |
| `deliveryReason` | enum: "not_enrolled" \| "install_blocked" \| "no_licence" \| "no_key" \| "unreachable" \| "rate_limited" \| "rejected" \| "mail_failed" \| "internal" \| null \| 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 POST "https://your-institute.example/api/v1/guardians/37386ae0-3738-7738-8386-37386ae03738/portal-invite" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "email": "ada.lovelace@example.com",
    "username": "string"
  }'
```

## POST /api/v1/guardians/{id}/portal-invite/resend

Send this guardian’s still-open portal invitation again. 409 when there is no pending invitation. On a cloud deployment the link is reissued (the previous one stops working); on a self-hosted install the invitation is re-sent through Institflow’s mailing service.

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

### Parameters

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

### Responses

**201** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `status` | enum: "invited" | yes |  |
| `email` | string (email) \| null | yes |  |
| `username` | string \| null | yes |  |
| `invitationId` | string (uuid) | yes |  |
| `expiresAt` | string (date-time) | yes |  |
| `deliveryState` | enum: "queued" \| "sent" \| "failed" | yes |  |
| `deliveryReason` | enum: "not_enrolled" \| "install_blocked" \| "no_licence" \| "no_key" \| "unreachable" \| "rate_limited" \| "rejected" \| "mail_failed" \| "internal" \| null \| 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 POST "https://your-institute.example/api/v1/guardians/37386ae0-3738-7738-8386-37386ae03738/portal-invite/resend" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

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

List every student linked to this guardian, with each link’s relationship metadata.

- Operation id: `GuardiansController_getStudents_v1`
- Permission: `guardians.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 |
| --- | --- | --- | --- |
| `data` | 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/guardians/37386ae0-3738-7738-8386-37386ae03738/students" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## GET /api/v1/guardians/count

Total number of guardians matching `q` — the list header’s record count.

- Operation id: `GuardiansController_count_v1`
- Permission: `guardians.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 |
| `q` | query | string | no | 1–200 chars |

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `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 GET "https://your-institute.example/api/v1/guardians/count" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## GET /api/v1/join-links

The institute's join links, newest first (cursor-paginated): label, target, uses, expiry and state (`active`, `paused`, `expired`, `used_up`, `revoked`). A public link carries its `joinUrl`; a private link never does after it was created.

- Operation id: `JoinLinksController_list_v1`
- Permission: `students.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 |

### 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/join-links" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## POST /api/v1/join-links

Create a public join link for students: anyone who opens it with a verified Institflow account becomes a pending request in the leads inbox. It expires (1-90 days, default 7) and has a use cap (1-500, default 30). Self-hosted installs only.

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

### Request body

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `label` | string | yes | 1–120 chars |
| `courseId` | string (uuid) | no |  |
| `groupId` | string (uuid) | no |  |
| `expiresInDays` | integer | no | 1–90 · default: 7 |
| `maxUses` | integer | no | 1–500 · default: 30 |

```json
{
  "label": "string",
  "courseId": "d0e4743a-d0e4-70e4-8e47-d0e4743ad0e4",
  "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
  "expiresInDays": 7,
  "maxUses": 30
}
```

### Responses

**201** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes |  |
| `kind` | enum: "public" \| "private" | yes |  |
| `label` | string \| null | yes |  |
| `state` | enum: "active" \| "paused" \| "expired" \| "used_up" \| "revoked" | yes |  |
| `status` | enum: "active" \| "paused" \| "revoked" | yes |  |
| `courseId` | string (uuid) \| null | yes |  |
| `courseName` | string \| null | yes |  |
| `groupId` | string (uuid) \| null | yes |  |
| `groupName` | string \| null | yes |  |
| `studentId` | string (uuid) \| null | yes |  |
| `guardianId` | string (uuid) \| null | yes |  |
| `personName` | string \| null | yes |  |
| `tokenHint` | string | yes |  |
| `expiresAt` | string (date-time) | yes |  |
| `maxUses` | integer | yes | -9007199254740991–9007199254740991 |
| `usedCount` | integer | yes | -9007199254740991–9007199254740991 |
| `createdAt` | string (date-time) | yes |  |
| `joinUrl` | 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 POST "https://your-institute.example/api/v1/join-links" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "label": "string",
    "courseId": "d0e4743a-d0e4-70e4-8e47-d0e4743ad0e4",
    "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
    "expiresInDays": 7,
    "maxUses": 30
  }'
```

## PATCH /api/v1/join-links/{id}

Pause, resume or revoke a join link. A revoked link cannot be used again (409).

- Operation id: `JoinLinksController_setStatus_v1`
- Permission: `students.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: "active" \| "paused" \| "revoked" | yes |  |

```json
{
  "status": "active"
}
```

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes |  |
| `kind` | enum: "public" \| "private" | yes |  |
| `label` | string \| null | yes |  |
| `state` | enum: "active" \| "paused" \| "expired" \| "used_up" \| "revoked" | yes |  |
| `status` | enum: "active" \| "paused" \| "revoked" | yes |  |
| `courseId` | string (uuid) \| null | yes |  |
| `courseName` | string \| null | yes |  |
| `groupId` | string (uuid) \| null | yes |  |
| `groupName` | string \| null | yes |  |
| `studentId` | string (uuid) \| null | yes |  |
| `guardianId` | string (uuid) \| null | yes |  |
| `personName` | string \| null | yes |  |
| `tokenHint` | string | yes |  |
| `expiresAt` | string (date-time) | yes |  |
| `maxUses` | integer | yes | -9007199254740991–9007199254740991 |
| `usedCount` | integer | yes | -9007199254740991–9007199254740991 |
| `createdAt` | string (date-time) | yes |  |
| `joinUrl` | 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 PATCH "https://your-institute.example/api/v1/join-links/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "status": "active"
  }'
```

## POST /api/v1/join-links/private

Create a single-use private link (7 days) for one student or guardian record. The link is in this response only: it is never stored. Creating another for the same record revokes the earlier ones. Self-hosted installs only.

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

### Request body

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `studentId` | string (uuid) | no |  |
| `guardianId` | string (uuid) | no |  |
| `sendEmail` | boolean | no | default: false |

```json
{
  "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
  "guardianId": "69993220-6999-7999-8993-699932206999",
  "sendEmail": false
}
```

### Responses

**201** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes |  |
| `kind` | enum: "public" \| "private" | yes |  |
| `label` | string \| null | yes |  |
| `state` | enum: "active" \| "paused" \| "expired" \| "used_up" \| "revoked" | yes |  |
| `status` | enum: "active" \| "paused" \| "revoked" | yes |  |
| `courseId` | string (uuid) \| null | yes |  |
| `courseName` | string \| null | yes |  |
| `groupId` | string (uuid) \| null | yes |  |
| `groupName` | string \| null | yes |  |
| `studentId` | string (uuid) \| null | yes |  |
| `guardianId` | string (uuid) \| null | yes |  |
| `personName` | string \| null | yes |  |
| `tokenHint` | string | yes |  |
| `expiresAt` | string (date-time) | yes |  |
| `maxUses` | integer | yes | -9007199254740991–9007199254740991 |
| `usedCount` | integer | yes | -9007199254740991–9007199254740991 |
| `createdAt` | string (date-time) | yes |  |
| `joinUrl` | 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 POST "https://your-institute.example/api/v1/join-links/private" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
    "guardianId": "69993220-6999-7999-8993-699932206999",
    "sendEmail": false
  }'
```

## GET /api/v1/join/{token}

What a join link is for, or why it cannot be used. Anonymous. A malformed, unknown or revoked token answers `unavailable` with no reason. Self-hosted installs only.

- Operation id: `JoinPublicController_describe_v1`
- Permission: —
- Auth: public (no credential)
- `Idempotency-Key`: not required

### Parameters

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

### Responses

**200** — Success.

```json
"value"
```

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/join/string" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## GET /api/v1/me/context

Everything the client needs on cold start: which identities the caller holds in this tenant, their children, their permissions, and the tenant/branch profile.

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

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `identities` | array<enum: "student" \| "guardian" \| "staff"> | yes |  |
| `studentId` | string (uuid) \| null | yes |  |
| `guardianId` | string (uuid) \| null | yes |  |
| `staffId` | string (uuid) \| null | yes |  |
| `staffKind` | enum: "teacher" \| "secretary" \| "manager" \| "other" \| null \| null | yes |  |
| `children` | array<object> | yes |  |
| `permissions` | array<string> | yes |  |
| `tenant` | object | yes |  |
| `branch` | object \| null | yes |  |
| `attendanceThresholds` | object | yes |  |
| `lastSeenAt` | string (date-time) \| 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/me/context" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## GET /api/v1/me/profile

The calling user’s own student profile (404 if they are not a student in this tenant).

- Operation id: `PortalController_getProfile_v1`
- Permission: `self.read`
- Auth: credential required
- `Idempotency-Key`: not required

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes |  |
| `branchId` | string (uuid) | yes |  |
| `studentNumber` | string | yes |  |
| `firstName` | string | yes |  |
| `lastName` | string | yes |  |
| `fullName` | string | yes |  |
| `dateOfBirth` | string (date) \| null | yes |  |
| `gender` | enum: "male" \| "female" \| "other" \| null \| null | yes |  |
| `nationalId` | string \| null | yes | ≥1 chars |
| `email` | string (email) \| null | yes |  |
| `phone` | string \| null | yes | ≥1 chars |
| `address` | object \| null | yes |  |
| `userId` | string (uuid) \| null | yes |  |
| `status` | enum: "lead" \| "active" \| "inactive" \| "graduated" \| "withdrawn" | yes |  |
| `notes` | string \| null | yes | 0–5000 chars |
| `tags` | array<string> | yes |  |
| `enrolledAt` | string (date) \| 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 GET "https://your-institute.example/api/v1/me/profile" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## GET /api/v1/me/students

The calling user’s linked children (404 if they are not a guardian in this tenant).

- Operation id: `PortalController_getStudents_v1`
- Permission: `children.read`
- Auth: credential required
- `Idempotency-Key`: not required

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `data` | 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/students" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## GET /api/v1/me/students/{studentId}/summary

One linked child’s summary (30-day attendance rate, outstanding balance, active groups, next session, recent-absence count) — the guardian-scoped mirror of `GET /students/{id}/summary`. A student may also call this for their own id.

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

### Parameters

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

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `studentId` | string (uuid) | yes |  |
| `attendance` | object | yes |  |
| `balances` | array<object> | yes |  |
| `activeGroups` | array<object> | yes |  |
| `enrollments` | array<StudentEnrollmentSummary> | yes |  |
| `recentSessions` | array<StudentRecentSession> | yes |  |
| `recentAbsences` | object | yes |  |
| `nextSession` | unknown \| null | yes |  |
| `assignments` | object | yes |  |
| `quizzes` | 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/students/a4a70332-a4a7-74a7-8a70-a4a70332a4a7/summary" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## GET /api/v1/staff

List staff (cursor-paginated), filterable by kind/branch/status.

- Operation id: `StaffController_list_v1`
- Permission: `staff.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 |
| `kind` | query | enum: "teacher" \| "secretary" \| "manager" \| "other" | no |  |
| `branchId` | query | string (uuid) | no |  |
| `status` | query | enum: "active" \| "inactive" | 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/staff" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## POST /api/v1/staff

Create a staff member (teacher/secretary/manager/other).

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

### Request body

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `branchId` | string (uuid) | no |  |
| `userId` | string (uuid) | no |  |
| `kind` | enum: "teacher" \| "secretary" \| "manager" \| "other" | yes |  |
| `firstName` | string | yes | 1–100 chars |
| `lastName` | string | yes | 1–100 chars |
| `phone` | string \| string | no |  |
| `email` | string \| string (email) | no |  |
| `specialties` | array<string> | no | default: [] |
| `hourlyRateMinor` | integer | no | 0–9007199254740991 |
| `status` | enum: "active" \| "inactive" | no | default: "active" |

```json
{
  "branchId": "390b8975-390b-790b-80b8-390b8975390b",
  "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
  "kind": "teacher",
  "firstName": "Ada",
  "lastName": "Lovelace",
  "phone": "+15551234567",
  "email": "ada.lovelace@example.com",
  "specialties": [
    "string"
  ],
  "hourlyRateMinor": 0,
  "status": "active"
}
```

### Responses

**201** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes |  |
| `branchId` | string (uuid) \| null | yes |  |
| `userId` | string (uuid) \| null | yes |  |
| `membershipId` | string (uuid) \| null | yes |  |
| `kind` | enum: "teacher" \| "secretary" \| "manager" \| "other" | yes |  |
| `firstName` | string | yes |  |
| `lastName` | string | yes |  |
| `phone` | string \| null | yes | ≥1 chars |
| `email` | string (email) \| null | yes |  |
| `specialties` | array<string> | yes |  |
| `hourlyRateMinor` | integer \| null | yes | -9007199254740991–9007199254740991 |
| `status` | enum: "active" \| "inactive" | 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/staff" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "branchId": "390b8975-390b-790b-80b8-390b8975390b",
    "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
    "kind": "teacher",
    "firstName": "Ada",
    "lastName": "Lovelace",
    "phone": "+15551234567",
    "email": "ada.lovelace@example.com",
    "specialties": [
      "string"
    ],
    "hourlyRateMinor": 0,
    "status": "active"
  }'
```

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

Delete a staff member (hard delete — `staff` has no soft-delete column).

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

### Parameters

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

### Responses

**200** — The staff member was deleted.

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/staff/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

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

Get one staff member by id.

- Operation id: `StaffController_getById_v1`
- Permission: `staff.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) \| null | yes |  |
| `userId` | string (uuid) \| null | yes |  |
| `membershipId` | string (uuid) \| null | yes |  |
| `kind` | enum: "teacher" \| "secretary" \| "manager" \| "other" | yes |  |
| `firstName` | string | yes |  |
| `lastName` | string | yes |  |
| `phone` | string \| null | yes | ≥1 chars |
| `email` | string (email) \| null | yes |  |
| `specialties` | array<string> | yes |  |
| `hourlyRateMinor` | integer \| null | yes | -9007199254740991–9007199254740991 |
| `status` | enum: "active" \| "inactive" | 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/staff/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

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

Update a staff member.

- Operation id: `StaffController_update_v1`
- Permission: `staff.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 |  |
| `userId` | string (uuid) | no |  |
| `kind` | enum: "teacher" \| "secretary" \| "manager" \| "other" | no |  |
| `firstName` | string | no | 1–100 chars |
| `lastName` | string | no | 1–100 chars |
| `phone` | string \| string | no |  |
| `email` | string \| string (email) | no |  |
| `specialties` | array<string> | no | default: [] |
| `hourlyRateMinor` | integer | no | 0–9007199254740991 |
| `status` | enum: "active" \| "inactive" | no | default: "active" |

```json
{
  "branchId": "390b8975-390b-790b-80b8-390b8975390b",
  "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
  "kind": "teacher",
  "firstName": "Ada",
  "lastName": "Lovelace",
  "phone": "+15551234567",
  "email": "ada.lovelace@example.com",
  "specialties": [
    "string"
  ],
  "hourlyRateMinor": 0,
  "status": "active"
}
```

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes |  |
| `branchId` | string (uuid) \| null | yes |  |
| `userId` | string (uuid) \| null | yes |  |
| `membershipId` | string (uuid) \| null | yes |  |
| `kind` | enum: "teacher" \| "secretary" \| "manager" \| "other" | yes |  |
| `firstName` | string | yes |  |
| `lastName` | string | yes |  |
| `phone` | string \| null | yes | ≥1 chars |
| `email` | string (email) \| null | yes |  |
| `specialties` | array<string> | yes |  |
| `hourlyRateMinor` | integer \| null | yes | -9007199254740991–9007199254740991 |
| `status` | enum: "active" \| "inactive" | 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/staff/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",
    "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
    "kind": "teacher",
    "firstName": "Ada",
    "lastName": "Lovelace",
    "phone": "+15551234567",
    "email": "ada.lovelace@example.com",
    "specialties": [
      "string"
    ],
    "hourlyRateMinor": 0,
    "status": "active"
  }'
```

## GET /api/v1/staff/{id}/summary

The groups this staff member teaches (with their weekly rules and active-student counts), their sessions for the coming week, how many students that adds up to, and how many past sessions they have not marked. `roles` carries the linked login’s role assignments for a caller holding `members.manage`, and is `null` otherwise. Requires `groups.read` as well as `staff.read`.

- Operation id: `StaffController_getSummary_v1`
- Permission: `staff.read`, `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 |
| --- | --- | --- | --- |
| `staffId` | string (uuid) | yes |  |
| `groups` | array<TaughtGroup> | yes |  |
| `upcomingSessions` | array<StaffSession> | yes |  |
| `studentCount` | integer | yes | 0–9007199254740991 |
| `unmarkedSessionCount` | integer | yes | 0–9007199254740991 |
| `membershipId` | string (uuid) \| null | yes |  |
| `roles` | array<object> \| 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/staff/37386ae0-3738-7738-8386-37386ae03738/summary" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## GET /api/v1/staff/count

Total number of staff matching the same filters as `GET /staff` (cursor/limit ignored) — the list header’s record count.

- Operation id: `StaffController_count_v1`
- Permission: `staff.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 |
| `kind` | query | enum: "teacher" \| "secretary" \| "manager" \| "other" | no |  |
| `branchId` | query | string (uuid) | no |  |
| `status` | query | enum: "active" \| "inactive" | no |  |

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `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 GET "https://your-institute.example/api/v1/staff/count" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## GET /api/v1/students

List students (cursor-paginated). `q` searches full name/student number/phone. A teacher without `students.write` only ever sees students in their own groups.

- Operation id: `StudentsController_list_v1`
- Permission: `students.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 |
| `q` | query | string | no | 1–200 chars |
| `status` | query | enum: "lead" \| "active" \| "inactive" \| "graduated" \| "withdrawn" | no |  |
| `branchId` | query | string (uuid) | no |  |
| `groupId` | query | string (uuid) | no |  |
| `tag` | query | string | no | 1–50 chars |

### 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" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## POST /api/v1/students

Create a student. `studentNumber` auto-allocates ("S-000001"-style, per tenant) when omitted.

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

### Request body

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `branchId` | string (uuid) | yes |  |
| `userId` | string (uuid) | no |  |
| `studentNumber` | enum: "" \| string | no |  |
| `firstName` | string | yes | 1–100 chars |
| `lastName` | string | yes | 1–100 chars |
| `dateOfBirth` | enum: "" \| string (date) | no |  |
| `gender` | enum: "male" \| "female" \| "other" | no |  |
| `nationalId` | enum: "" \| string | no |  |
| `email` | enum: "" \| string (email) | no |  |
| `phone` | enum: "" \| string | no |  |
| `address` | object | no |  |
| `status` | enum: "lead" \| "active" \| "inactive" \| "graduated" \| "withdrawn" | no | default: "lead" |
| `notes` | string | no | 0–2000 chars |
| `tags` | array<string> | no | default: [] |
| `enrolledAt` | enum: "" \| string (date) | no |  |

```json
{
  "branchId": "390b8975-390b-790b-80b8-390b8975390b",
  "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
  "studentNumber": "",
  "firstName": "Ada",
  "lastName": "Lovelace",
  "dateOfBirth": "",
  "gender": "male",
  "nationalId": "",
  "email": "",
  "phone": "",
  "address": {},
  "status": "lead",
  "notes": "Prefers afternoon sessions.",
  "tags": [
    "string"
  ],
  "enrolledAt": ""
}
```

### Responses

**201** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes |  |
| `branchId` | string (uuid) | yes |  |
| `studentNumber` | string | yes |  |
| `firstName` | string | yes |  |
| `lastName` | string | yes |  |
| `fullName` | string | yes |  |
| `dateOfBirth` | string (date) \| null | yes |  |
| `gender` | enum: "male" \| "female" \| "other" \| null \| null | yes |  |
| `nationalId` | string \| null | yes | ≥1 chars |
| `email` | string (email) \| null | yes |  |
| `phone` | string \| null | yes | ≥1 chars |
| `address` | object \| null | yes |  |
| `userId` | string (uuid) \| null | yes |  |
| `status` | enum: "lead" \| "active" \| "inactive" \| "graduated" \| "withdrawn" | yes |  |
| `notes` | string \| null | yes | 0–5000 chars |
| `tags` | array<string> | yes |  |
| `enrolledAt` | string (date) \| 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 POST "https://your-institute.example/api/v1/students" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "branchId": "390b8975-390b-790b-80b8-390b8975390b",
    "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
    "studentNumber": "",
    "firstName": "Ada",
    "lastName": "Lovelace",
    "dateOfBirth": "",
    "gender": "male",
    "nationalId": "",
    "email": "",
    "phone": "",
    "address": {},
    "status": "lead",
    "notes": "Prefers afternoon sessions.",
    "tags": [
      "string"
    ],
    "enrolledAt": ""
  }'
```

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

Soft-delete a student (excluded from reads/lists by default afterwards).

- Operation id: `StudentsController_remove_v1`
- Permission: `students.delete`
- Auth: credential required
- `Idempotency-Key`: required for API-key callers

### Parameters

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

### Responses

**200** — The student was deleted.

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/students/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

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

Get one student by id.

- Operation id: `StudentsController_getById_v1`
- Permission: `students.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 |  |
| `studentNumber` | string | yes |  |
| `firstName` | string | yes |  |
| `lastName` | string | yes |  |
| `fullName` | string | yes |  |
| `dateOfBirth` | string (date) \| null | yes |  |
| `gender` | enum: "male" \| "female" \| "other" \| null \| null | yes |  |
| `nationalId` | string \| null | yes | ≥1 chars |
| `email` | string (email) \| null | yes |  |
| `phone` | string \| null | yes | ≥1 chars |
| `address` | object \| null | yes |  |
| `userId` | string (uuid) \| null | yes |  |
| `status` | enum: "lead" \| "active" \| "inactive" \| "graduated" \| "withdrawn" | yes |  |
| `notes` | string \| null | yes | 0–5000 chars |
| `tags` | array<string> | yes |  |
| `enrolledAt` | string (date) \| 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 GET "https://your-institute.example/api/v1/students/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

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

Update a student. A `status` change is validated against the allowed transition graph, audited, and emits `student.status_changed`.

- Operation id: `StudentsController_update_v1`
- Permission: `students.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 |  |
| `userId` | string (uuid) | no |  |
| `studentNumber` | enum: "" \| string | no |  |
| `firstName` | string | no | 1–100 chars |
| `lastName` | string | no | 1–100 chars |
| `dateOfBirth` | enum: "" \| string (date) | no |  |
| `gender` | enum: "male" \| "female" \| "other" | no |  |
| `nationalId` | enum: "" \| string | no |  |
| `email` | enum: "" \| string (email) | no |  |
| `phone` | enum: "" \| string | no |  |
| `address` | object | no |  |
| `status` | enum: "lead" \| "active" \| "inactive" \| "graduated" \| "withdrawn" | no | default: "lead" |
| `notes` | string | no | 0–2000 chars |
| `tags` | array<string> | no | default: [] |
| `enrolledAt` | enum: "" \| string (date) | no |  |

```json
{
  "branchId": "390b8975-390b-790b-80b8-390b8975390b",
  "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
  "studentNumber": "",
  "firstName": "Ada",
  "lastName": "Lovelace",
  "dateOfBirth": "",
  "gender": "male",
  "nationalId": "",
  "email": "",
  "phone": "",
  "address": {},
  "status": "lead",
  "notes": "Prefers afternoon sessions.",
  "tags": [
    "string"
  ],
  "enrolledAt": ""
}
```

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes |  |
| `branchId` | string (uuid) | yes |  |
| `studentNumber` | string | yes |  |
| `firstName` | string | yes |  |
| `lastName` | string | yes |  |
| `fullName` | string | yes |  |
| `dateOfBirth` | string (date) \| null | yes |  |
| `gender` | enum: "male" \| "female" \| "other" \| null \| null | yes |  |
| `nationalId` | string \| null | yes | ≥1 chars |
| `email` | string (email) \| null | yes |  |
| `phone` | string \| null | yes | ≥1 chars |
| `address` | object \| null | yes |  |
| `userId` | string (uuid) \| null | yes |  |
| `status` | enum: "lead" \| "active" \| "inactive" \| "graduated" \| "withdrawn" | yes |  |
| `notes` | string \| null | yes | 0–5000 chars |
| `tags` | array<string> | yes |  |
| `enrolledAt` | string (date) \| 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/students/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",
    "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
    "studentNumber": "",
    "firstName": "Ada",
    "lastName": "Lovelace",
    "dateOfBirth": "",
    "gender": "male",
    "nationalId": "",
    "email": "",
    "phone": "",
    "address": {},
    "status": "lead",
    "notes": "Prefers afternoon sessions.",
    "tags": [
      "string"
    ],
    "enrolledAt": ""
  }'
```

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

Open invoices (remaining/overdue) and the recurring obligation behind each active enrollment — the student profile’s Financial card. Requires `invoices.read` as well as `students.read`.

- Operation id: `StudentsController_getFinance_v1`
- Permission: `students.read`, `invoices.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 |
| --- | --- | --- | --- |
| `studentId` | string (uuid) | yes |  |
| `obligations` | array<StudentObligation> | yes |  |
| `monthlyTotals` | array<object> | yes |  |
| `openInvoices` | array<OpenInvoiceSummary> | yes |  |
| `openInvoiceTotals` | array<OpenInvoiceTotals> | 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/finance" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

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

List every guardian linked to this student, with each link’s relationship metadata.

- Operation id: `StudentsController_getGuardians_v1`
- Permission: `students.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 |
| --- | --- | --- | --- |
| `data` | 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/students/37386ae0-3738-7738-8386-37386ae03738/guardians" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## POST /api/v1/students/{id}/guardians

Link an existing guardian to a student (relationship, isPrimary, isPayer). Exactly one guardian may be primary per student.

- Operation id: `StudentsController_linkGuardian_v1`
- Permission: `students.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 |
| --- | --- | --- | --- |
| `guardianId` | string (uuid) | yes |  |
| `relationship` | enum: "father" \| "mother" \| "sibling" \| "other" | yes |  |
| `isPrimary` | boolean | no | default: false |
| `isPayer` | boolean | no | default: false |

```json
{
  "guardianId": "69993220-6999-7999-8993-699932206999",
  "relationship": "father",
  "isPrimary": false,
  "isPayer": false
}
```

### Responses

**201** — The guardian was linked.

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/students/37386ae0-3738-7738-8386-37386ae03738/guardians" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "guardianId": "69993220-6999-7999-8993-699932206999",
    "relationship": "father",
    "isPrimary": false,
    "isPayer": false
  }'
```

## DELETE /api/v1/students/{id}/guardians/{guardianId}

Unlink a guardian from a student.

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

### Parameters

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

### Responses

**200** — The guardian was unlinked.

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/students/37386ae0-3738-7738-8386-37386ae03738/guardians/69993220-6999-7999-8993-699932206999" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## GET /api/v1/students/{id}/portal-invite

The newest still-open portal invitation for this student and whether its email has left (`deliveryState`: queued, sent or failed, with a `deliveryReason`), or `invitation: null`.

- Operation id: `StudentsController_portalInviteStatus_v1`
- Permission: `students.write`
- Auth: credential required
- `Idempotency-Key`: not required

### Parameters

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

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `invitation` | object \| 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/portal-invite" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## POST /api/v1/students/{id}/portal-invite

Invite this student to the portal: creates (or reuses) their login and links it to this record. Defaults to the student’s own email address.

- Operation id: `StudentsController_portalInvite_v1`
- Permission: `students.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 |
| --- | --- | --- | --- |
| `email` | string \| string (email) | no |  |
| `username` | string \| string | no |  |

```json
{
  "email": "ada.lovelace@example.com",
  "username": "string"
}
```

### Responses

**201** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `status` | enum: "invited" | yes |  |
| `email` | string (email) \| null | yes |  |
| `username` | string \| null | yes |  |
| `invitationId` | string (uuid) | yes |  |
| `expiresAt` | string (date-time) | yes |  |
| `deliveryState` | enum: "queued" \| "sent" \| "failed" | yes |  |
| `deliveryReason` | enum: "not_enrolled" \| "install_blocked" \| "no_licence" \| "no_key" \| "unreachable" \| "rate_limited" \| "rejected" \| "mail_failed" \| "internal" \| null \| 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 POST "https://your-institute.example/api/v1/students/37386ae0-3738-7738-8386-37386ae03738/portal-invite" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "email": "ada.lovelace@example.com",
    "username": "string"
  }'
```

## POST /api/v1/students/{id}/portal-invite/resend

Send this student’s still-open portal invitation again. 409 when there is no pending invitation. On a cloud deployment the link is reissued (the previous one stops working); on a self-hosted install the invitation is re-sent through Institflow’s mailing service.

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

### Parameters

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

### Responses

**201** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `status` | enum: "invited" | yes |  |
| `email` | string (email) \| null | yes |  |
| `username` | string \| null | yes |  |
| `invitationId` | string (uuid) | yes |  |
| `expiresAt` | string (date-time) | yes |  |
| `deliveryState` | enum: "queued" \| "sent" \| "failed" | yes |  |
| `deliveryReason` | enum: "not_enrolled" \| "install_blocked" \| "no_licence" \| "no_key" \| "unreachable" \| "rate_limited" \| "rejected" \| "mail_failed" \| "internal" \| null \| 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 POST "https://your-institute.example/api/v1/students/37386ae0-3738-7738-8386-37386ae03738/portal-invite/resend" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## PATCH /api/v1/students/{id}/status

Change only a student’s status, enforcing the allowed transition graph.

- Operation id: `StudentsController_changeStatus_v1`
- Permission: `students.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: "lead" \| "active" \| "inactive" \| "graduated" \| "withdrawn" | yes |  |

```json
{
  "status": "lead"
}
```

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `id` | string (uuid) | yes |  |
| `branchId` | string (uuid) | yes |  |
| `studentNumber` | string | yes |  |
| `firstName` | string | yes |  |
| `lastName` | string | yes |  |
| `fullName` | string | yes |  |
| `dateOfBirth` | string (date) \| null | yes |  |
| `gender` | enum: "male" \| "female" \| "other" \| null \| null | yes |  |
| `nationalId` | string \| null | yes | ≥1 chars |
| `email` | string (email) \| null | yes |  |
| `phone` | string \| null | yes | ≥1 chars |
| `address` | object \| null | yes |  |
| `userId` | string (uuid) \| null | yes |  |
| `status` | enum: "lead" \| "active" \| "inactive" \| "graduated" \| "withdrawn" | yes |  |
| `notes` | string \| null | yes | 0–5000 chars |
| `tags` | array<string> | yes |  |
| `enrolledAt` | string (date) \| 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/students/37386ae0-3738-7738-8386-37386ae03738/status" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "status": "lead"
  }'
```

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

Attendance rate (last 30 days), outstanding balance, active groups, next session, and recent-absence count for one student.

- Operation id: `StudentsController_getSummary_v1`
- Permission: `students.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 |
| --- | --- | --- | --- |
| `studentId` | string (uuid) | yes |  |
| `attendance` | object | yes |  |
| `balances` | array<object> | yes |  |
| `activeGroups` | array<object> | yes |  |
| `enrollments` | array<StudentEnrollmentSummary> | yes |  |
| `recentSessions` | array<StudentRecentSession> | yes |  |
| `recentAbsences` | object | yes |  |
| `nextSession` | unknown \| null | yes |  |
| `assignments` | object | yes |  |
| `quizzes` | 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/students/37386ae0-3738-7738-8386-37386ae03738/summary" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## GET /api/v1/students/count

Total number of students matching the same filters as `GET /students` (cursor/limit ignored) — the list header’s record count.

- Operation id: `StudentsController_count_v1`
- Permission: `students.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 |
| `q` | query | string | no | 1–200 chars |
| `status` | query | enum: "lead" \| "active" \| "inactive" \| "graduated" \| "withdrawn" | no |  |
| `branchId` | query | string (uuid) | no |  |
| `groupId` | query | string (uuid) | no |  |
| `tag` | query | string | no | 1–50 chars |

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `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 GET "https://your-institute.example/api/v1/students/count" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```

## GET /api/v1/students/roster-stats

Active groups, 30-day attendance rate and (with `invoices.read`) outstanding balance for up to 100 students in one request — the students list’s operational columns. `balances` is `null` when the caller lacks `invoices.read`.

- Operation id: `StudentsController_rosterStats_v1`
- Permission: `students.read`
- Auth: credential required
- `Idempotency-Key`: not required

### Parameters

| Name | In | Type | Required | Description |
| --- | --- | --- | --- | --- |
| `ids` | query | string | yes | ≥1 chars |

### Responses

**200** — Success.

| Property | Type | Required | Constraints |
| --- | --- | --- | --- |
| `data` | array<StudentRosterStatsEntry> | 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/roster-stats?ids=string" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"
```
