people

People

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

34 endpoints

GET /api/v1/guardians

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

Requires authenticationScopeguardians.read

Path & query parameters

NameInTypeConstraints
cursor
querystring
limit
queryinteger1–100 · default: 25
q
querystring1–200 chars

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/guardians" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200
NameTypeConstraints
datarequired
array<object>
nextCursorrequired
string | null
json
{
  "data": [
    {
      "id": "37386ae0-3738-7738-8386-37386ae03738",
      "firstName": "Ada",
      "lastName": "Lovelace",
      "phone": "+15551234567",
      "email": "ada.lovelace@example.com",
      "nationalId": "X123456789",
      "preferredLocale": "ar",
      "address": {},
      "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
      "createdAt": "2026-03-02T09:00:00.000Z",
      "updatedAt": "2026-03-02T09:00:00.000Z"
    }
  ],
  "nextCursor": null
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

POST /api/v1/guardians

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

Requires authenticationScopeguardians.write

Request body

NameTypeConstraints
userId
string (uuid)
firstNamerequired
string1–100 chars
lastNamerequired
string1–100 chars
phonerequired
string
email
enum: "" | string (email)
nationalId
enum: "" | string
preferredLocale
enum: "ar" | "en" | "he"default: "ar"
address
object

Example

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

curl example

curl
curl -X POST "https://api.yourdomain.com/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": {}
  }'

Responses

201
NameTypeConstraints
idrequired
string (uuid)
firstNamerequired
string
lastNamerequired
string
phonerequired
string
emailrequired
string (email) | null
nationalIdrequired
string | null≥1 chars
preferredLocalerequired
enum: "ar" | "en" | "he"
addressrequired
object | null
userIdrequired
string (uuid) | null
createdAtrequired
string (date-time)
updatedAtrequired
string (date-time)
json
{
  "id": "37386ae0-3738-7738-8386-37386ae03738",
  "firstName": "Ada",
  "lastName": "Lovelace",
  "phone": "+15551234567",
  "email": "ada.lovelace@example.com",
  "nationalId": "X123456789",
  "preferredLocale": "ar",
  "address": {},
  "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
  "createdAt": "2026-03-02T09:00:00.000Z",
  "updatedAt": "2026-03-02T09:00:00.000Z"
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

428An API-key caller must send an `Idempotency-Key` header (`IDEMPOTENCY_KEY_REQUIRED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

DELETE /api/v1/guardians/{id}

Soft-delete a guardian.

Requires authenticationScopeguardians.write

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

curl example

curl
curl -X DELETE "https://api.yourdomain.com/api/v1/guardians/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200The guardian was deleted.

The API's OpenAPI spec doesn't declare a typed schema for this response yet — it's still JSON, just not documented here.

400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

428An API-key caller must send an `Idempotency-Key` header (`IDEMPOTENCY_KEY_REQUIRED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

GET /api/v1/guardians/{id}

Get one guardian by id.

Requires authenticationScopeguardians.read

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/guardians/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200
NameTypeConstraints
idrequired
string (uuid)
firstNamerequired
string
lastNamerequired
string
phonerequired
string
emailrequired
string (email) | null
nationalIdrequired
string | null≥1 chars
preferredLocalerequired
enum: "ar" | "en" | "he"
addressrequired
object | null
userIdrequired
string (uuid) | null
createdAtrequired
string (date-time)
updatedAtrequired
string (date-time)
json
{
  "id": "37386ae0-3738-7738-8386-37386ae03738",
  "firstName": "Ada",
  "lastName": "Lovelace",
  "phone": "+15551234567",
  "email": "ada.lovelace@example.com",
  "nationalId": "X123456789",
  "preferredLocale": "ar",
  "address": {},
  "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
  "createdAt": "2026-03-02T09:00:00.000Z",
  "updatedAt": "2026-03-02T09:00:00.000Z"
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

PATCH /api/v1/guardians/{id}

Update a guardian.

Requires authenticationScopeguardians.write

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

Request body

NameTypeConstraints
userId
string (uuid)
firstName
string1–100 chars
lastName
string1–100 chars
phone
string
email
enum: "" | string (email)
nationalId
enum: "" | string
preferredLocale
enum: "ar" | "en" | "he"default: "ar"
address
object

Example

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

curl example

curl
curl -X PATCH "https://api.yourdomain.com/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": {}
  }'

Responses

200
NameTypeConstraints
idrequired
string (uuid)
firstNamerequired
string
lastNamerequired
string
phonerequired
string
emailrequired
string (email) | null
nationalIdrequired
string | null≥1 chars
preferredLocalerequired
enum: "ar" | "en" | "he"
addressrequired
object | null
userIdrequired
string (uuid) | null
createdAtrequired
string (date-time)
updatedAtrequired
string (date-time)
json
{
  "id": "37386ae0-3738-7738-8386-37386ae03738",
  "firstName": "Ada",
  "lastName": "Lovelace",
  "phone": "+15551234567",
  "email": "ada.lovelace@example.com",
  "nationalId": "X123456789",
  "preferredLocale": "ar",
  "address": {},
  "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
  "createdAt": "2026-03-02T09:00:00.000Z",
  "updatedAt": "2026-03-02T09:00:00.000Z"
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

428An API-key caller must send an `Idempotency-Key` header (`IDEMPOTENCY_KEY_REQUIRED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

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

Requires authenticationScopeguardians.read

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/guardians/37386ae0-3738-7738-8386-37386ae03738/family-summary" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200
NameTypeConstraints
guardianIdrequired
string (uuid)
childrenrequired
array<FamilyChild>
financialrequired
object | null
json
{
  "guardianId": "69993220-6999-7999-8993-699932206999",
  "children": [
    {
      "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
      "fullName": "Ada Lovelace",
      "studentNumber": "S-000042",
      "status": "lead",
      "relationship": "father",
      "isPrimary": true,
      "isPayer": true,
      "phone": "+15551234567",
      "activeGroups": [
        {
          "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
          "name": "Algebra I",
          "status": "planned"
        }
      ],
      "attendanceRatePercent": 0,
      "balances": [
        {
          "currency": "ILS",
          "outstandingMinor": -9007199254740991,
          "overdueCount": 0
        }
      ]
    }
  ],
  "financial": {
    "acrossChildren": [
      {
        "currency": "ILS",
        "outstandingMinor": -9007199254740991,
        "overdueCount": 0
      }
    ],
    "payableByGuardian": [
      {
        "currency": "ILS",
        "count": 0,
        "remainingMinor": -9007199254740991,
        "overdueCount": 0,
        "overdueRemainingMinor": -9007199254740991
      }
    ],
    "openInvoices": [
      {
        "invoiceId": "f3093e02-f309-7309-8093-f3093e02f309",
        "number": "string",
        "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
        "studentName": "string",
        "status": "draft",
        "dueDate": "2026-03-02",
        "currency": "ILS",
        "totalMinor": 12000,
        "paidMinor": 12000,
        "remainingMinor": -9007199254740991,
        "isOverdue": true
      }
    ]
  }
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

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.

Requires authenticationScopeguardians.write

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

Request body

NameTypeConstraints
email
string | string (email)

Example

json
{
  "email": "ada.lovelace@example.com"
}

curl example

curl
curl -X POST "https://api.yourdomain.com/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"
  }'

Responses

201
NameTypeConstraints
statusrequired
enum: "invited"
emailrequired
string (email)
invitationIdrequired
string (uuid)
expiresAtrequired
string (date-time)
json
{
  "status": "invited",
  "email": "ada.lovelace@example.com",
  "invitationId": "00637243-0063-7063-8637-006372430063",
  "expiresAt": "2026-03-02T09:00:00.000Z"
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

428An API-key caller must send an `Idempotency-Key` header (`IDEMPOTENCY_KEY_REQUIRED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

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

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

Requires authenticationScopeguardians.read

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/guardians/37386ae0-3738-7738-8386-37386ae03738/students" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200
NameTypeConstraints
datarequired
array<object>
json
{
  "data": [
    {
      "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
      "firstName": "Ada",
      "lastName": "Lovelace",
      "fullName": "Ada Lovelace",
      "status": "lead",
      "relationship": "father",
      "isPrimary": true,
      "isPayer": true
    }
  ]
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

GET /api/v1/guardians/count

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

Requires authenticationScopeguardians.read

Path & query parameters

NameInTypeConstraints
cursor
querystring
limit
queryinteger1–100 · default: 25
q
querystring1–200 chars

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/guardians/count" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200
NameTypeConstraints
totalrequired
integer0–9007199254740991
json
{
  "total": 0
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

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.

Requires authenticationScopeNo additional permission

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/me/context" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200
NameTypeConstraints
identitiesrequired
array<enum: "student" | "guardian" | "staff">
studentIdrequired
string (uuid) | null
guardianIdrequired
string (uuid) | null
staffIdrequired
string (uuid) | null
staffKindrequired
enum: "teacher" | "secretary" | "manager" | "other" | null | null
childrenrequired
array<object>
permissionsrequired
array<string>
tenantrequired
object
branchrequired
object | null
json
{
  "identities": [
    "student"
  ],
  "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
  "guardianId": "69993220-6999-7999-8993-699932206999",
  "staffId": "489fc644-489f-789f-89fc-489fc644489f",
  "staffKind": "teacher",
  "children": [
    {
      "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
      "firstName": "Ada",
      "lastName": "Lovelace",
      "fullName": "Ada Lovelace",
      "status": "lead",
      "branchId": "390b8975-390b-790b-80b8-390b8975390b"
    }
  ],
  "permissions": [
    "string"
  ],
  "tenant": {
    "id": "37386ae0-3738-7738-8386-37386ae03738",
    "slug": "string",
    "name": "Algebra I",
    "locale": "ar",
    "timezone": "string",
    "currency": "USD"
  },
  "branch": {
    "id": "37386ae0-3738-7738-8386-37386ae03738",
    "name": "Algebra I"
  }
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

GET /api/v1/me/profile

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

Requires authenticationScopeself.read

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/me/profile" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200
NameTypeConstraints
idrequired
string (uuid)
branchIdrequired
string (uuid)
studentNumberrequired
string
firstNamerequired
string
lastNamerequired
string
fullNamerequired
string
dateOfBirthrequired
string (date) | null
genderrequired
enum: "male" | "female" | "other" | null | null
nationalIdrequired
string | null≥1 chars
emailrequired
string (email) | null
phonerequired
string | null≥1 chars
addressrequired
object | null
userIdrequired
string (uuid) | null
statusrequired
enum: "lead" | "active" | "inactive" | "graduated" | "withdrawn"
notesrequired
string | null0–5000 chars
tagsrequired
array<string>
enrolledAtrequired
string (date) | null
createdAtrequired
string (date-time)
updatedAtrequired
string (date-time)
json
{
  "id": "37386ae0-3738-7738-8386-37386ae03738",
  "branchId": "390b8975-390b-790b-80b8-390b8975390b",
  "studentNumber": "S-000042",
  "firstName": "Ada",
  "lastName": "Lovelace",
  "fullName": "Ada Lovelace",
  "dateOfBirth": "2026-03-02",
  "gender": "male",
  "nationalId": "X123456789",
  "email": "ada.lovelace@example.com",
  "phone": "+15551234567",
  "address": {},
  "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
  "status": "lead",
  "notes": "Prefers afternoon sessions.",
  "tags": [
    "string"
  ],
  "enrolledAt": "2026-03-02",
  "createdAt": "2026-03-02T09:00:00.000Z",
  "updatedAt": "2026-03-02T09:00:00.000Z"
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

GET /api/v1/me/students

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

Requires authenticationScopechildren.read

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/me/students" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200
NameTypeConstraints
datarequired
array<object>
json
{
  "data": [
    {
      "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
      "firstName": "Ada",
      "lastName": "Lovelace",
      "fullName": "Ada Lovelace",
      "status": "lead",
      "relationship": "father",
      "isPrimary": true,
      "isPayer": true
    }
  ]
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

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

One linked child’s summary (30-day attendance rate, outstanding balance, active groups) — the guardian-scoped mirror of `GET /students/{id}/summary`.

Requires authenticationScopechildren.read

Path & query parameters

NameInTypeConstraints
studentIdrequired
pathstring

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/me/students/a4a70332-a4a7-74a7-8a70-a4a70332a4a7/summary" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200
NameTypeConstraints
studentIdrequired
string (uuid)
attendancerequired
object
balancesrequired
array<object>
activeGroupsrequired
array<object>
enrollmentsrequired
array<StudentEnrollmentSummary>
recentSessionsrequired
array<StudentRecentSession>
json
{
  "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
  "attendance": {
    "last30Days": {
      "presentCount": 0,
      "absentCount": 0,
      "lateCount": 0,
      "excusedCount": 0,
      "ratePercent": 0
    }
  },
  "balances": [
    {
      "currency": "ILS",
      "outstandingMinor": -9007199254740991
    }
  ],
  "activeGroups": [
    {
      "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
      "name": "Algebra I",
      "status": "planned"
    }
  ],
  "enrollments": [
    {
      "enrollmentId": "c8bc7863-c8bc-78bc-8bc7-c8bc7863c8bc",
      "status": "pending",
      "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
      "groupName": "string",
      "groupStatus": "planned",
      "courseId": "d0e4743a-d0e4-70e4-8e47-d0e4743ad0e4",
      "courseName": "string",
      "room": "string",
      "teacherStaffId": "22f9eb14-22f9-72f9-8f9e-22f9eb1422f9",
      "teacherName": "string",
      "schedule": [
        {
          "weekday": 0,
          "startTime": "string",
          "endTime": "string"
        }
      ],
      "nextSession": {
        "sessionId": "736a51cd-736a-736a-86a5-736a51cd736a",
        "scheduledStart": "2026-03-02T09:00:00.000Z",
        "scheduledEnd": "2026-03-02T09:00:00.000Z"
      }
    }
  ],
  "recentSessions": [
    {
      "sessionId": "736a51cd-736a-736a-86a5-736a51cd736a",
      "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
      "groupName": "string",
      "scheduledStart": "2026-03-02T09:00:00.000Z",
      "status": "present",
      "minutesLate": 0
    }
  ]
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

GET /api/v1/staff

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

Requires authenticationScopestaff.read

Path & query parameters

NameInTypeConstraints
cursor
querystring
limit
queryinteger1–100 · default: 25
kind
queryenum: "teacher" | "secretary" | "manager" | "other"
branchId
querystring (uuid)
status
queryenum: "active" | "inactive"

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/staff" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200
NameTypeConstraints
datarequired
array<object>
nextCursorrequired
string | null
json
{
  "data": [
    {
      "id": "37386ae0-3738-7738-8386-37386ae03738",
      "branchId": "390b8975-390b-790b-80b8-390b8975390b",
      "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
      "membershipId": "e34c26d7-e34c-734c-84c2-e34c26d7e34c",
      "kind": "teacher",
      "firstName": "Ada",
      "lastName": "Lovelace",
      "phone": "+15551234567",
      "email": "ada.lovelace@example.com",
      "specialties": [
        "string"
      ],
      "hourlyRateMinor": -9007199254740991,
      "status": "active",
      "createdAt": "2026-03-02T09:00:00.000Z",
      "updatedAt": "2026-03-02T09:00:00.000Z"
    }
  ],
  "nextCursor": null
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

POST /api/v1/staff

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

Requires authenticationScopestaff.write

Request body

NameTypeConstraints
branchId
string (uuid)
userId
string (uuid)
kindrequired
enum: "teacher" | "secretary" | "manager" | "other"
firstNamerequired
string1–100 chars
lastNamerequired
string1–100 chars
phone
string | string
email
string | string (email)
specialties
array<string>default: []
hourlyRateMinor
integer0–9007199254740991
status
enum: "active" | "inactive"default: "active"

Example

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"
}

curl example

curl
curl -X POST "https://api.yourdomain.com/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"
  }'

Responses

201
NameTypeConstraints
idrequired
string (uuid)
branchIdrequired
string (uuid) | null
userIdrequired
string (uuid) | null
membershipIdrequired
string (uuid) | null
kindrequired
enum: "teacher" | "secretary" | "manager" | "other"
firstNamerequired
string
lastNamerequired
string
phonerequired
string | null≥1 chars
emailrequired
string (email) | null
specialtiesrequired
array<string>
hourlyRateMinorrequired
integer | null-9007199254740991–9007199254740991
statusrequired
enum: "active" | "inactive"
createdAtrequired
string (date-time)
updatedAtrequired
string (date-time)
json
{
  "id": "37386ae0-3738-7738-8386-37386ae03738",
  "branchId": "390b8975-390b-790b-80b8-390b8975390b",
  "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
  "membershipId": "e34c26d7-e34c-734c-84c2-e34c26d7e34c",
  "kind": "teacher",
  "firstName": "Ada",
  "lastName": "Lovelace",
  "phone": "+15551234567",
  "email": "ada.lovelace@example.com",
  "specialties": [
    "string"
  ],
  "hourlyRateMinor": -9007199254740991,
  "status": "active",
  "createdAt": "2026-03-02T09:00:00.000Z",
  "updatedAt": "2026-03-02T09:00:00.000Z"
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

428An API-key caller must send an `Idempotency-Key` header (`IDEMPOTENCY_KEY_REQUIRED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

DELETE /api/v1/staff/{id}

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

Requires authenticationScopestaff.write

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

curl example

curl
curl -X DELETE "https://api.yourdomain.com/api/v1/staff/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200The staff member was deleted.

The API's OpenAPI spec doesn't declare a typed schema for this response yet — it's still JSON, just not documented here.

400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

428An API-key caller must send an `Idempotency-Key` header (`IDEMPOTENCY_KEY_REQUIRED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

GET /api/v1/staff/{id}

Get one staff member by id.

Requires authenticationScopestaff.read

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/staff/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200
NameTypeConstraints
idrequired
string (uuid)
branchIdrequired
string (uuid) | null
userIdrequired
string (uuid) | null
membershipIdrequired
string (uuid) | null
kindrequired
enum: "teacher" | "secretary" | "manager" | "other"
firstNamerequired
string
lastNamerequired
string
phonerequired
string | null≥1 chars
emailrequired
string (email) | null
specialtiesrequired
array<string>
hourlyRateMinorrequired
integer | null-9007199254740991–9007199254740991
statusrequired
enum: "active" | "inactive"
createdAtrequired
string (date-time)
updatedAtrequired
string (date-time)
json
{
  "id": "37386ae0-3738-7738-8386-37386ae03738",
  "branchId": "390b8975-390b-790b-80b8-390b8975390b",
  "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
  "membershipId": "e34c26d7-e34c-734c-84c2-e34c26d7e34c",
  "kind": "teacher",
  "firstName": "Ada",
  "lastName": "Lovelace",
  "phone": "+15551234567",
  "email": "ada.lovelace@example.com",
  "specialties": [
    "string"
  ],
  "hourlyRateMinor": -9007199254740991,
  "status": "active",
  "createdAt": "2026-03-02T09:00:00.000Z",
  "updatedAt": "2026-03-02T09:00:00.000Z"
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

PATCH /api/v1/staff/{id}

Update a staff member.

Requires authenticationScopestaff.write

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

Request body

NameTypeConstraints
branchId
string (uuid)
userId
string (uuid)
kind
enum: "teacher" | "secretary" | "manager" | "other"
firstName
string1–100 chars
lastName
string1–100 chars
phone
string | string
email
string | string (email)
specialties
array<string>default: []
hourlyRateMinor
integer0–9007199254740991
status
enum: "active" | "inactive"default: "active"

Example

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"
}

curl example

curl
curl -X PATCH "https://api.yourdomain.com/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"
  }'

Responses

200
NameTypeConstraints
idrequired
string (uuid)
branchIdrequired
string (uuid) | null
userIdrequired
string (uuid) | null
membershipIdrequired
string (uuid) | null
kindrequired
enum: "teacher" | "secretary" | "manager" | "other"
firstNamerequired
string
lastNamerequired
string
phonerequired
string | null≥1 chars
emailrequired
string (email) | null
specialtiesrequired
array<string>
hourlyRateMinorrequired
integer | null-9007199254740991–9007199254740991
statusrequired
enum: "active" | "inactive"
createdAtrequired
string (date-time)
updatedAtrequired
string (date-time)
json
{
  "id": "37386ae0-3738-7738-8386-37386ae03738",
  "branchId": "390b8975-390b-790b-80b8-390b8975390b",
  "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
  "membershipId": "e34c26d7-e34c-734c-84c2-e34c26d7e34c",
  "kind": "teacher",
  "firstName": "Ada",
  "lastName": "Lovelace",
  "phone": "+15551234567",
  "email": "ada.lovelace@example.com",
  "specialties": [
    "string"
  ],
  "hourlyRateMinor": -9007199254740991,
  "status": "active",
  "createdAt": "2026-03-02T09:00:00.000Z",
  "updatedAt": "2026-03-02T09:00:00.000Z"
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

428An API-key caller must send an `Idempotency-Key` header (`IDEMPOTENCY_KEY_REQUIRED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

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

Requires authenticationScopestaff.readgroups.read

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/staff/37386ae0-3738-7738-8386-37386ae03738/summary" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200
NameTypeConstraints
staffIdrequired
string (uuid)
groupsrequired
array<TaughtGroup>
upcomingSessionsrequired
array<StaffSession>
studentCountrequired
integer0–9007199254740991
unmarkedSessionCountrequired
integer0–9007199254740991
membershipIdrequired
string (uuid) | null
rolesrequired
array<object> | null
json
{
  "staffId": "489fc644-489f-789f-89fc-489fc644489f",
  "groups": [
    {
      "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
      "name": "Algebra I",
      "status": "planned",
      "room": "string",
      "courseId": "d0e4743a-d0e4-70e4-8e47-d0e4743ad0e4",
      "courseName": "string",
      "studentCount": 0,
      "schedule": [
        {
          "weekday": 0,
          "startTime": "string",
          "endTime": "string"
        }
      ]
    }
  ],
  "upcomingSessions": [
    {
      "sessionId": "736a51cd-736a-736a-86a5-736a51cd736a",
      "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
      "groupName": "string",
      "courseName": "string",
      "room": "string",
      "scheduledStart": "2026-03-02T09:00:00.000Z",
      "scheduledEnd": "2026-03-02T09:00:00.000Z",
      "status": "scheduled",
      "isSubstitute": true
    }
  ],
  "studentCount": 0,
  "unmarkedSessionCount": 0,
  "membershipId": "e34c26d7-e34c-734c-84c2-e34c26d7e34c",
  "roles": [
    {
      "roleId": "5b007a17-5b00-7b00-8007-5b007a175b00",
      "roleName": "string",
      "roleKey": "string",
      "branchId": "390b8975-390b-790b-80b8-390b8975390b"
    }
  ]
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

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.

Requires authenticationScopestaff.read

Path & query parameters

NameInTypeConstraints
cursor
querystring
limit
queryinteger1–100 · default: 25
kind
queryenum: "teacher" | "secretary" | "manager" | "other"
branchId
querystring (uuid)
status
queryenum: "active" | "inactive"

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/staff/count" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200
NameTypeConstraints
totalrequired
integer0–9007199254740991
json
{
  "total": 0
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

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.

Requires authenticationScopestudents.read

Path & query parameters

NameInTypeConstraints
cursor
querystring
limit
queryinteger1–100 · default: 25
q
querystring1–200 chars
status
queryenum: "lead" | "active" | "inactive" | "graduated" | "withdrawn"
branchId
querystring (uuid)
groupId
querystring (uuid)
tag
querystring1–50 chars

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/students" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200
NameTypeConstraints
datarequired
array<object>
nextCursorrequired
string | null
json
{
  "data": [
    {
      "id": "37386ae0-3738-7738-8386-37386ae03738",
      "branchId": "390b8975-390b-790b-80b8-390b8975390b",
      "studentNumber": "S-000042",
      "firstName": "Ada",
      "lastName": "Lovelace",
      "fullName": "Ada Lovelace",
      "dateOfBirth": "2026-03-02",
      "gender": "male",
      "nationalId": "X123456789",
      "email": "ada.lovelace@example.com",
      "phone": "+15551234567",
      "address": {},
      "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
      "status": "lead",
      "notes": "Prefers afternoon sessions.",
      "tags": [
        "string"
      ],
      "enrolledAt": "2026-03-02",
      "createdAt": "2026-03-02T09:00:00.000Z",
      "updatedAt": "2026-03-02T09:00:00.000Z"
    }
  ],
  "nextCursor": null
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

POST /api/v1/students

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

Requires authenticationScopestudents.write

Request body

NameTypeConstraints
branchIdrequired
string (uuid)
userId
string (uuid)
studentNumber
enum: "" | string
firstNamerequired
string1–100 chars
lastNamerequired
string1–100 chars
dateOfBirth
enum: "" | string (date)
gender
enum: "male" | "female" | "other"
nationalId
enum: "" | string
email
enum: "" | string (email)
phone
enum: "" | string
address
object
status
enum: "lead" | "active" | "inactive" | "graduated" | "withdrawn"default: "lead"
notes
string0–2000 chars
tags
array<string>default: []
enrolledAt
enum: "" | string (date)

Example

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": ""
}

curl example

curl
curl -X POST "https://api.yourdomain.com/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": ""
  }'

Responses

201
NameTypeConstraints
idrequired
string (uuid)
branchIdrequired
string (uuid)
studentNumberrequired
string
firstNamerequired
string
lastNamerequired
string
fullNamerequired
string
dateOfBirthrequired
string (date) | null
genderrequired
enum: "male" | "female" | "other" | null | null
nationalIdrequired
string | null≥1 chars
emailrequired
string (email) | null
phonerequired
string | null≥1 chars
addressrequired
object | null
userIdrequired
string (uuid) | null
statusrequired
enum: "lead" | "active" | "inactive" | "graduated" | "withdrawn"
notesrequired
string | null0–5000 chars
tagsrequired
array<string>
enrolledAtrequired
string (date) | null
createdAtrequired
string (date-time)
updatedAtrequired
string (date-time)
json
{
  "id": "37386ae0-3738-7738-8386-37386ae03738",
  "branchId": "390b8975-390b-790b-80b8-390b8975390b",
  "studentNumber": "S-000042",
  "firstName": "Ada",
  "lastName": "Lovelace",
  "fullName": "Ada Lovelace",
  "dateOfBirth": "2026-03-02",
  "gender": "male",
  "nationalId": "X123456789",
  "email": "ada.lovelace@example.com",
  "phone": "+15551234567",
  "address": {},
  "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
  "status": "lead",
  "notes": "Prefers afternoon sessions.",
  "tags": [
    "string"
  ],
  "enrolledAt": "2026-03-02",
  "createdAt": "2026-03-02T09:00:00.000Z",
  "updatedAt": "2026-03-02T09:00:00.000Z"
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

428An API-key caller must send an `Idempotency-Key` header (`IDEMPOTENCY_KEY_REQUIRED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

DELETE /api/v1/students/{id}

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

Requires authenticationScopestudents.delete

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

curl example

curl
curl -X DELETE "https://api.yourdomain.com/api/v1/students/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200The student was deleted.

The API's OpenAPI spec doesn't declare a typed schema for this response yet — it's still JSON, just not documented here.

400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

428An API-key caller must send an `Idempotency-Key` header (`IDEMPOTENCY_KEY_REQUIRED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

GET /api/v1/students/{id}

Get one student by id.

Requires authenticationScopestudents.read

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/students/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200
NameTypeConstraints
idrequired
string (uuid)
branchIdrequired
string (uuid)
studentNumberrequired
string
firstNamerequired
string
lastNamerequired
string
fullNamerequired
string
dateOfBirthrequired
string (date) | null
genderrequired
enum: "male" | "female" | "other" | null | null
nationalIdrequired
string | null≥1 chars
emailrequired
string (email) | null
phonerequired
string | null≥1 chars
addressrequired
object | null
userIdrequired
string (uuid) | null
statusrequired
enum: "lead" | "active" | "inactive" | "graduated" | "withdrawn"
notesrequired
string | null0–5000 chars
tagsrequired
array<string>
enrolledAtrequired
string (date) | null
createdAtrequired
string (date-time)
updatedAtrequired
string (date-time)
json
{
  "id": "37386ae0-3738-7738-8386-37386ae03738",
  "branchId": "390b8975-390b-790b-80b8-390b8975390b",
  "studentNumber": "S-000042",
  "firstName": "Ada",
  "lastName": "Lovelace",
  "fullName": "Ada Lovelace",
  "dateOfBirth": "2026-03-02",
  "gender": "male",
  "nationalId": "X123456789",
  "email": "ada.lovelace@example.com",
  "phone": "+15551234567",
  "address": {},
  "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
  "status": "lead",
  "notes": "Prefers afternoon sessions.",
  "tags": [
    "string"
  ],
  "enrolledAt": "2026-03-02",
  "createdAt": "2026-03-02T09:00:00.000Z",
  "updatedAt": "2026-03-02T09:00:00.000Z"
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

PATCH /api/v1/students/{id}

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

Requires authenticationScopestudents.write

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

Request body

NameTypeConstraints
branchId
string (uuid)
userId
string (uuid)
studentNumber
enum: "" | string
firstName
string1–100 chars
lastName
string1–100 chars
dateOfBirth
enum: "" | string (date)
gender
enum: "male" | "female" | "other"
nationalId
enum: "" | string
email
enum: "" | string (email)
phone
enum: "" | string
address
object
status
enum: "lead" | "active" | "inactive" | "graduated" | "withdrawn"default: "lead"
notes
string0–2000 chars
tags
array<string>default: []
enrolledAt
enum: "" | string (date)

Example

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": ""
}

curl example

curl
curl -X PATCH "https://api.yourdomain.com/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": ""
  }'

Responses

200
NameTypeConstraints
idrequired
string (uuid)
branchIdrequired
string (uuid)
studentNumberrequired
string
firstNamerequired
string
lastNamerequired
string
fullNamerequired
string
dateOfBirthrequired
string (date) | null
genderrequired
enum: "male" | "female" | "other" | null | null
nationalIdrequired
string | null≥1 chars
emailrequired
string (email) | null
phonerequired
string | null≥1 chars
addressrequired
object | null
userIdrequired
string (uuid) | null
statusrequired
enum: "lead" | "active" | "inactive" | "graduated" | "withdrawn"
notesrequired
string | null0–5000 chars
tagsrequired
array<string>
enrolledAtrequired
string (date) | null
createdAtrequired
string (date-time)
updatedAtrequired
string (date-time)
json
{
  "id": "37386ae0-3738-7738-8386-37386ae03738",
  "branchId": "390b8975-390b-790b-80b8-390b8975390b",
  "studentNumber": "S-000042",
  "firstName": "Ada",
  "lastName": "Lovelace",
  "fullName": "Ada Lovelace",
  "dateOfBirth": "2026-03-02",
  "gender": "male",
  "nationalId": "X123456789",
  "email": "ada.lovelace@example.com",
  "phone": "+15551234567",
  "address": {},
  "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
  "status": "lead",
  "notes": "Prefers afternoon sessions.",
  "tags": [
    "string"
  ],
  "enrolledAt": "2026-03-02",
  "createdAt": "2026-03-02T09:00:00.000Z",
  "updatedAt": "2026-03-02T09:00:00.000Z"
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

428An API-key caller must send an `Idempotency-Key` header (`IDEMPOTENCY_KEY_REQUIRED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

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

Requires authenticationScopestudents.readinvoices.read

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/students/37386ae0-3738-7738-8386-37386ae03738/finance" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200
NameTypeConstraints
studentIdrequired
string (uuid)
obligationsrequired
array<StudentObligation>
monthlyTotalsrequired
array<object>
openInvoicesrequired
array<OpenInvoiceSummary>
openInvoiceTotalsrequired
array<OpenInvoiceTotals>
json
{
  "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
  "obligations": [
    {
      "enrollmentId": "c8bc7863-c8bc-78bc-8bc7-c8bc7863c8bc",
      "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
      "groupName": "string",
      "courseId": "d0e4743a-d0e4-70e4-8e47-d0e4743ad0e4",
      "courseName": "string",
      "amountMinor": 12000,
      "currency": "ILS",
      "source": "fee_schedule",
      "recurrence": "once",
      "feeScheduleId": "3cfbbfe7-3cfb-7cfb-8fbb-3cfbbfe73cfb",
      "feeScheduleName": "string",
      "nextDueOn": "2026-03-02"
    }
  ],
  "monthlyTotals": [
    {
      "currency": "ILS",
      "amountMinor": 12000
    }
  ],
  "openInvoices": [
    {
      "invoiceId": "f3093e02-f309-7309-8093-f3093e02f309",
      "number": "string",
      "status": "draft",
      "issueDate": "2026-03-02",
      "dueDate": "2026-03-02",
      "currency": "ILS",
      "totalMinor": 12000,
      "paidMinor": 12000,
      "remainingMinor": -9007199254740991,
      "isOverdue": true
    }
  ],
  "openInvoiceTotals": [
    {
      "currency": "ILS",
      "count": 0,
      "remainingMinor": -9007199254740991,
      "overdueCount": 0,
      "overdueRemainingMinor": -9007199254740991
    }
  ]
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

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

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

Requires authenticationScopestudents.read

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/students/37386ae0-3738-7738-8386-37386ae03738/guardians" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200
NameTypeConstraints
datarequired
array<object>
json
{
  "data": [
    {
      "guardianId": "69993220-6999-7999-8993-699932206999",
      "firstName": "Ada",
      "lastName": "Lovelace",
      "phone": "+15551234567",
      "email": "ada.lovelace@example.com",
      "relationship": "father",
      "isPrimary": true,
      "isPayer": true
    }
  ]
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

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

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

Requires authenticationScopestudents.write

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

Request body

NameTypeConstraints
guardianIdrequired
string (uuid)
relationshiprequired
enum: "father" | "mother" | "sibling" | "other"
isPrimary
booleandefault: false
isPayer
booleandefault: false

Example

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

curl example

curl
curl -X POST "https://api.yourdomain.com/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
  }'

Responses

201The guardian was linked.

The API's OpenAPI spec doesn't declare a typed schema for this response yet — it's still JSON, just not documented here.

400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

428An API-key caller must send an `Idempotency-Key` header (`IDEMPOTENCY_KEY_REQUIRED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

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

Unlink a guardian from a student.

Requires authenticationScopestudents.write

Path & query parameters

NameInTypeConstraints
idrequired
pathstring
guardianIdrequired
pathstring

curl example

curl
curl -X DELETE "https://api.yourdomain.com/api/v1/students/37386ae0-3738-7738-8386-37386ae03738/guardians/69993220-6999-7999-8993-699932206999" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200The guardian was unlinked.

The API's OpenAPI spec doesn't declare a typed schema for this response yet — it's still JSON, just not documented here.

400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

428An API-key caller must send an `Idempotency-Key` header (`IDEMPOTENCY_KEY_REQUIRED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

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.

Requires authenticationScopestudents.write

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

Request body

NameTypeConstraints
email
string | string (email)

Example

json
{
  "email": "ada.lovelace@example.com"
}

curl example

curl
curl -X POST "https://api.yourdomain.com/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"
  }'

Responses

201
NameTypeConstraints
statusrequired
enum: "invited"
emailrequired
string (email)
invitationIdrequired
string (uuid)
expiresAtrequired
string (date-time)
json
{
  "status": "invited",
  "email": "ada.lovelace@example.com",
  "invitationId": "00637243-0063-7063-8637-006372430063",
  "expiresAt": "2026-03-02T09:00:00.000Z"
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

428An API-key caller must send an `Idempotency-Key` header (`IDEMPOTENCY_KEY_REQUIRED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

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

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

Requires authenticationScopestudents.write

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

Request body

NameTypeConstraints
statusrequired
enum: "lead" | "active" | "inactive" | "graduated" | "withdrawn"

Example

json
{
  "status": "lead"
}

curl example

curl
curl -X PATCH "https://api.yourdomain.com/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"
  }'

Responses

200
NameTypeConstraints
idrequired
string (uuid)
branchIdrequired
string (uuid)
studentNumberrequired
string
firstNamerequired
string
lastNamerequired
string
fullNamerequired
string
dateOfBirthrequired
string (date) | null
genderrequired
enum: "male" | "female" | "other" | null | null
nationalIdrequired
string | null≥1 chars
emailrequired
string (email) | null
phonerequired
string | null≥1 chars
addressrequired
object | null
userIdrequired
string (uuid) | null
statusrequired
enum: "lead" | "active" | "inactive" | "graduated" | "withdrawn"
notesrequired
string | null0–5000 chars
tagsrequired
array<string>
enrolledAtrequired
string (date) | null
createdAtrequired
string (date-time)
updatedAtrequired
string (date-time)
json
{
  "id": "37386ae0-3738-7738-8386-37386ae03738",
  "branchId": "390b8975-390b-790b-80b8-390b8975390b",
  "studentNumber": "S-000042",
  "firstName": "Ada",
  "lastName": "Lovelace",
  "fullName": "Ada Lovelace",
  "dateOfBirth": "2026-03-02",
  "gender": "male",
  "nationalId": "X123456789",
  "email": "ada.lovelace@example.com",
  "phone": "+15551234567",
  "address": {},
  "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
  "status": "lead",
  "notes": "Prefers afternoon sessions.",
  "tags": [
    "string"
  ],
  "enrolledAt": "2026-03-02",
  "createdAt": "2026-03-02T09:00:00.000Z",
  "updatedAt": "2026-03-02T09:00:00.000Z"
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

428An API-key caller must send an `Idempotency-Key` header (`IDEMPOTENCY_KEY_REQUIRED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

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

Attendance rate (last 30 days), outstanding balance, and active groups for one student.

Requires authenticationScopestudents.read

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/students/37386ae0-3738-7738-8386-37386ae03738/summary" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200
NameTypeConstraints
studentIdrequired
string (uuid)
attendancerequired
object
balancesrequired
array<object>
activeGroupsrequired
array<object>
enrollmentsrequired
array<StudentEnrollmentSummary>
recentSessionsrequired
array<StudentRecentSession>
json
{
  "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
  "attendance": {
    "last30Days": {
      "presentCount": 0,
      "absentCount": 0,
      "lateCount": 0,
      "excusedCount": 0,
      "ratePercent": 0
    }
  },
  "balances": [
    {
      "currency": "ILS",
      "outstandingMinor": -9007199254740991
    }
  ],
  "activeGroups": [
    {
      "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
      "name": "Algebra I",
      "status": "planned"
    }
  ],
  "enrollments": [
    {
      "enrollmentId": "c8bc7863-c8bc-78bc-8bc7-c8bc7863c8bc",
      "status": "pending",
      "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
      "groupName": "string",
      "groupStatus": "planned",
      "courseId": "d0e4743a-d0e4-70e4-8e47-d0e4743ad0e4",
      "courseName": "string",
      "room": "string",
      "teacherStaffId": "22f9eb14-22f9-72f9-8f9e-22f9eb1422f9",
      "teacherName": "string",
      "schedule": [
        {
          "weekday": 0,
          "startTime": "string",
          "endTime": "string"
        }
      ],
      "nextSession": {
        "sessionId": "736a51cd-736a-736a-86a5-736a51cd736a",
        "scheduledStart": "2026-03-02T09:00:00.000Z",
        "scheduledEnd": "2026-03-02T09:00:00.000Z"
      }
    }
  ],
  "recentSessions": [
    {
      "sessionId": "736a51cd-736a-736a-86a5-736a51cd736a",
      "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
      "groupName": "string",
      "scheduledStart": "2026-03-02T09:00:00.000Z",
      "status": "present",
      "minutesLate": 0
    }
  ]
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

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.

Requires authenticationScopestudents.read

Path & query parameters

NameInTypeConstraints
cursor
querystring
limit
queryinteger1–100 · default: 25
q
querystring1–200 chars
status
queryenum: "lead" | "active" | "inactive" | "graduated" | "withdrawn"
branchId
querystring (uuid)
groupId
querystring (uuid)
tag
querystring1–50 chars

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/students/count" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200
NameTypeConstraints
totalrequired
integer0–9007199254740991
json
{
  "total": 0
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

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

Requires authenticationScopestudents.read

Path & query parameters

NameInTypeConstraints
idsrequired
querystring≥1 chars

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/students/roster-stats?ids=string" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200
NameTypeConstraints
datarequired
array<StudentRosterStatsEntry>
json
{
  "data": [
    {
      "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
      "activeGroups": [
        {
          "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
          "name": "Algebra I",
          "status": "planned"
        }
      ],
      "attendanceRatePercent": 0,
      "balances": [
        {
          "currency": "ILS",
          "outstandingMinor": -9007199254740991,
          "overdueCount": 0
        }
      ]
    }
  ]
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

401Missing, expired or invalid credentials (`UNAUTHORIZED`, `TOKEN_INVALID`, `SESSION_REVOKED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

402The tenant’s subscription or license is locked (`PAYMENT_REQUIRED`, `SUBSCRIPTION_SUSPENDED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

403Authenticated, but not allowed (`FORBIDDEN`, `FEATURE_DISABLED`, `QUOTA_EXCEEDED`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

404No such resource, or it is not visible to this tenant (`NOT_FOUND`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

409Conflicts with current state (`CONFLICT`, `VERSION_CONFLICT`, `IDEMPOTENCY_KEY_REUSED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

422The request was read but its content is not acceptable: body/query/path/header failed schema validation (`VALIDATION_ERROR`, with `details.issues`), or a domain rule rejected a well-formed value (`VIDEO_URL_INVALID`, …).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

429Rate limit exceeded — see the `RateLimit-*` and `Retry-After` headers (`RATE_LIMITED`).

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.

500Unexpected server error (`INTERNAL_ERROR`). Never carries a stack trace or SQL.

Returns the shared `ErrorEnvelope` — `{ error: { code, message, requestId, details? } }`. Every code is listed in the Conventions guide.