people

People

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

43 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://your-institute.example/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://your-institute.example/api/v1/guardians" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
    "firstName": "Ada",
    "lastName": "Lovelace",
    "phone": "+15551234567",
    "email": "",
    "nationalId": "",
    "preferredLocale": "ar",
    "address": {}
  }'

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://your-institute.example/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://your-institute.example/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://your-institute.example/api/v1/guardians/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
    "firstName": "Ada",
    "lastName": "Lovelace",
    "phone": "+15551234567",
    "email": "",
    "nationalId": "",
    "preferredLocale": "ar",
    "address": {}
  }'

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://your-institute.example/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.

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

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

Requires authenticationScopeguardians.write

Path & query parameters

NameInTypeConstraints
idrequired
pathstring—

curl example

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

Responses

200
NameTypeConstraints
invitationrequired
object | null—
json
{
  "invitation": {
    "invitationId": "00637243-0063-7063-8637-006372430063",
    "email": "ada.lovelace@example.com",
    "username": "string",
    "expiresAt": "2026-03-02T09:00:00.000Z",
    "deliveryState": "queued",
    "deliveryReason": "not_enrolled",
    "deliveryUpdatedAt": "2026-03-02T09:00:00.000Z",
    "response": "pending",
    "declinedAt": "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.

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)—
username
string | string—

Example

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

curl example

curl
curl -X POST "https://your-institute.example/api/v1/guardians/37386ae0-3738-7738-8386-37386ae03738/portal-invite" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "email": "ada.lovelace@example.com",
    "username": "string"
  }'

Responses

201
NameTypeConstraints
statusrequired
enum: "invited"—
emailrequired
string (email) | null—
usernamerequired
string | null—
invitationIdrequired
string (uuid)—
expiresAtrequired
string (date-time)—
deliveryStaterequired
enum: "queued" | "sent" | "failed"—
deliveryReasonrequired
enum: "not_enrolled" | "install_blocked" | "no_licence" | "no_key" | "unreachable" | "rate_limited" | "rejected" | "mail_failed" | "internal" | null | null—
json
{
  "status": "invited",
  "email": "ada.lovelace@example.com",
  "username": "string",
  "invitationId": "00637243-0063-7063-8637-006372430063",
  "expiresAt": "2026-03-02T09:00:00.000Z",
  "deliveryState": "queued",
  "deliveryReason": "not_enrolled"
}
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/guardians/{id}/portal-invite/resend

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

Requires authenticationScopeguardians.write

Path & query parameters

NameInTypeConstraints
idrequired
pathstring—

curl example

curl
curl -X POST "https://your-institute.example/api/v1/guardians/37386ae0-3738-7738-8386-37386ae03738/portal-invite/resend" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

201
NameTypeConstraints
statusrequired
enum: "invited"—
emailrequired
string (email) | null—
usernamerequired
string | null—
invitationIdrequired
string (uuid)—
expiresAtrequired
string (date-time)—
deliveryStaterequired
enum: "queued" | "sent" | "failed"—
deliveryReasonrequired
enum: "not_enrolled" | "install_blocked" | "no_licence" | "no_key" | "unreachable" | "rate_limited" | "rejected" | "mail_failed" | "internal" | null | null—
json
{
  "status": "invited",
  "email": "ada.lovelace@example.com",
  "username": "string",
  "invitationId": "00637243-0063-7063-8637-006372430063",
  "expiresAt": "2026-03-02T09:00:00.000Z",
  "deliveryState": "queued",
  "deliveryReason": "not_enrolled"
}
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://your-institute.example/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://your-institute.example/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.

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

Requires authenticationScopestudents.read

Path & query parameters

NameInTypeConstraints
cursor
querystring—
limit
queryinteger1–100 · default: 25

curl example

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

Responses

200
NameTypeConstraints
datarequired
array<object>—
nextCursorrequired
string | null—
json
{
  "data": [
    {
      "id": "37386ae0-3738-7738-8386-37386ae03738",
      "kind": "public",
      "label": "string",
      "state": "active",
      "status": "active",
      "courseId": "d0e4743a-d0e4-70e4-8e47-d0e4743ad0e4",
      "courseName": "string",
      "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
      "groupName": "string",
      "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
      "guardianId": "69993220-6999-7999-8993-699932206999",
      "personName": "string",
      "tokenHint": "string",
      "expiresAt": "2026-03-02T09:00:00.000Z",
      "maxUses": -9007199254740991,
      "usedCount": -9007199254740991,
      "createdAt": "2026-03-02T09:00:00.000Z",
      "joinUrl": "string"
    }
  ],
  "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/join-links

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

Requires authenticationScopestudents.write

Request body

NameTypeConstraints
labelrequired
string1–120 chars
courseId
string (uuid)—
groupId
string (uuid)—
expiresInDays
integer1–90 · default: 7
maxUses
integer1–500 · default: 30

Example

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

curl example

curl
curl -X POST "https://your-institute.example/api/v1/join-links" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "label": "string",
    "courseId": "d0e4743a-d0e4-70e4-8e47-d0e4743ad0e4",
    "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
    "expiresInDays": 7,
    "maxUses": 30
  }'

Responses

201
NameTypeConstraints
idrequired
string (uuid)—
kindrequired
enum: "public" | "private"—
labelrequired
string | null—
staterequired
enum: "active" | "paused" | "expired" | "used_up" | "revoked"—
statusrequired
enum: "active" | "paused" | "revoked"—
courseIdrequired
string (uuid) | null—
courseNamerequired
string | null—
groupIdrequired
string (uuid) | null—
groupNamerequired
string | null—
studentIdrequired
string (uuid) | null—
guardianIdrequired
string (uuid) | null—
personNamerequired
string | null—
tokenHintrequired
string—
expiresAtrequired
string (date-time)—
maxUsesrequired
integer-9007199254740991–9007199254740991
usedCountrequired
integer-9007199254740991–9007199254740991
createdAtrequired
string (date-time)—
joinUrlrequired
string | null—
json
{
  "id": "37386ae0-3738-7738-8386-37386ae03738",
  "kind": "public",
  "label": "string",
  "state": "active",
  "status": "active",
  "courseId": "d0e4743a-d0e4-70e4-8e47-d0e4743ad0e4",
  "courseName": "string",
  "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
  "groupName": "string",
  "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
  "guardianId": "69993220-6999-7999-8993-699932206999",
  "personName": "string",
  "tokenHint": "string",
  "expiresAt": "2026-03-02T09:00:00.000Z",
  "maxUses": -9007199254740991,
  "usedCount": -9007199254740991,
  "createdAt": "2026-03-02T09:00:00.000Z",
  "joinUrl": "string"
}
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.

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

Requires authenticationScopestudents.write

Path & query parameters

NameInTypeConstraints
idrequired
pathstring—

Request body

NameTypeConstraints
statusrequired
enum: "active" | "paused" | "revoked"—

Example

json
{
  "status": "active"
}

curl example

curl
curl -X PATCH "https://your-institute.example/api/v1/join-links/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "status": "active"
  }'

Responses

200
NameTypeConstraints
idrequired
string (uuid)—
kindrequired
enum: "public" | "private"—
labelrequired
string | null—
staterequired
enum: "active" | "paused" | "expired" | "used_up" | "revoked"—
statusrequired
enum: "active" | "paused" | "revoked"—
courseIdrequired
string (uuid) | null—
courseNamerequired
string | null—
groupIdrequired
string (uuid) | null—
groupNamerequired
string | null—
studentIdrequired
string (uuid) | null—
guardianIdrequired
string (uuid) | null—
personNamerequired
string | null—
tokenHintrequired
string—
expiresAtrequired
string (date-time)—
maxUsesrequired
integer-9007199254740991–9007199254740991
usedCountrequired
integer-9007199254740991–9007199254740991
createdAtrequired
string (date-time)—
joinUrlrequired
string | null—
json
{
  "id": "37386ae0-3738-7738-8386-37386ae03738",
  "kind": "public",
  "label": "string",
  "state": "active",
  "status": "active",
  "courseId": "d0e4743a-d0e4-70e4-8e47-d0e4743ad0e4",
  "courseName": "string",
  "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
  "groupName": "string",
  "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
  "guardianId": "69993220-6999-7999-8993-699932206999",
  "personName": "string",
  "tokenHint": "string",
  "expiresAt": "2026-03-02T09:00:00.000Z",
  "maxUses": -9007199254740991,
  "usedCount": -9007199254740991,
  "createdAt": "2026-03-02T09:00:00.000Z",
  "joinUrl": "string"
}
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/join-links/private

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

Requires authenticationScopestudents.write

Request body

NameTypeConstraints
studentId
string (uuid)—
guardianId
string (uuid)—
sendEmail
booleandefault: false

Example

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

curl example

curl
curl -X POST "https://your-institute.example/api/v1/join-links/private" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
    "guardianId": "69993220-6999-7999-8993-699932206999",
    "sendEmail": false
  }'

Responses

201
NameTypeConstraints
idrequired
string (uuid)—
kindrequired
enum: "public" | "private"—
labelrequired
string | null—
staterequired
enum: "active" | "paused" | "expired" | "used_up" | "revoked"—
statusrequired
enum: "active" | "paused" | "revoked"—
courseIdrequired
string (uuid) | null—
courseNamerequired
string | null—
groupIdrequired
string (uuid) | null—
groupNamerequired
string | null—
studentIdrequired
string (uuid) | null—
guardianIdrequired
string (uuid) | null—
personNamerequired
string | null—
tokenHintrequired
string—
expiresAtrequired
string (date-time)—
maxUsesrequired
integer-9007199254740991–9007199254740991
usedCountrequired
integer-9007199254740991–9007199254740991
createdAtrequired
string (date-time)—
joinUrlrequired
string | null—
json
{
  "id": "37386ae0-3738-7738-8386-37386ae03738",
  "kind": "public",
  "label": "string",
  "state": "active",
  "status": "active",
  "courseId": "d0e4743a-d0e4-70e4-8e47-d0e4743ad0e4",
  "courseName": "string",
  "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
  "groupName": "string",
  "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
  "guardianId": "69993220-6999-7999-8993-699932206999",
  "personName": "string",
  "tokenHint": "string",
  "expiresAt": "2026-03-02T09:00:00.000Z",
  "maxUses": -9007199254740991,
  "usedCount": -9007199254740991,
  "createdAt": "2026-03-02T09:00:00.000Z",
  "joinUrl": "string"
}
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/join/{token}

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

No authenticationScopePublic

Path & query parameters

NameInTypeConstraints
tokenrequired
pathstring—

curl example

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

Responses

200
json
"value"
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.

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://your-institute.example/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—
attendanceThresholdsrequired
object—
lastSeenAtrequired
string (date-time) | 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"
  },
  "attendanceThresholds": {
    "warnBelowRate": 0,
    "criticalBelowRate": 0
  },
  "lastSeenAt": "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/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://your-institute.example/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://your-institute.example/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, next session, recent-absence count) — the guardian-scoped mirror of `GET /students/{id}/summary`. A student may also call this for their own id.

Requires authenticationScopeNo additional permission

Path & query parameters

NameInTypeConstraints
studentIdrequired
pathstring—

curl example

curl
curl -X GET "https://your-institute.example/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>—
recentAbsencesrequired
object—
nextSessionrequired
unknown | null—
assignmentsrequired
object—
quizzesrequired
object—
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
    }
  ],
  "recentAbsences": {
    "absent": 0,
    "ofLast": 0
  },
  "nextSession": "value",
  "assignments": {
    "dueSoon": 0,
    "late": 0
  },
  "quizzes": {
    "dueSoon": 0,
    "late": 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://your-institute.example/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://your-institute.example/api/v1/staff" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "branchId": "390b8975-390b-790b-80b8-390b8975390b",
    "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
    "kind": "teacher",
    "firstName": "Ada",
    "lastName": "Lovelace",
    "phone": "+15551234567",
    "email": "ada.lovelace@example.com",
    "specialties": [
      "string"
    ],
    "hourlyRateMinor": 0,
    "status": "active"
  }'

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://your-institute.example/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://your-institute.example/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://your-institute.example/api/v1/staff/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "branchId": "390b8975-390b-790b-80b8-390b8975390b",
    "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
    "kind": "teacher",
    "firstName": "Ada",
    "lastName": "Lovelace",
    "phone": "+15551234567",
    "email": "ada.lovelace@example.com",
    "specialties": [
      "string"
    ],
    "hourlyRateMinor": 0,
    "status": "active"
  }'

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://your-institute.example/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://your-institute.example/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://your-institute.example/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://your-institute.example/api/v1/students" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "branchId": "390b8975-390b-790b-80b8-390b8975390b",
    "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
    "studentNumber": "",
    "firstName": "Ada",
    "lastName": "Lovelace",
    "dateOfBirth": "",
    "gender": "male",
    "nationalId": "",
    "email": "",
    "phone": "",
    "address": {},
    "status": "lead",
    "notes": "Prefers afternoon sessions.",
    "tags": [
      "string"
    ],
    "enrolledAt": ""
  }'

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://your-institute.example/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://your-institute.example/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://your-institute.example/api/v1/students/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "branchId": "390b8975-390b-790b-80b8-390b8975390b",
    "userId": "11f967df-11f9-71f9-8f96-11f967df11f9",
    "studentNumber": "",
    "firstName": "Ada",
    "lastName": "Lovelace",
    "dateOfBirth": "",
    "gender": "male",
    "nationalId": "",
    "email": "",
    "phone": "",
    "address": {},
    "status": "lead",
    "notes": "Prefers afternoon sessions.",
    "tags": [
      "string"
    ],
    "enrolledAt": ""
  }'

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

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://your-institute.example/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.

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

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

Requires authenticationScopestudents.write

Path & query parameters

NameInTypeConstraints
idrequired
pathstring—

curl example

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

Responses

200
NameTypeConstraints
invitationrequired
object | null—
json
{
  "invitation": {
    "invitationId": "00637243-0063-7063-8637-006372430063",
    "email": "ada.lovelace@example.com",
    "username": "string",
    "expiresAt": "2026-03-02T09:00:00.000Z",
    "deliveryState": "queued",
    "deliveryReason": "not_enrolled",
    "deliveryUpdatedAt": "2026-03-02T09:00:00.000Z",
    "response": "pending",
    "declinedAt": "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.

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)—
username
string | string—

Example

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

curl example

curl
curl -X POST "https://your-institute.example/api/v1/students/37386ae0-3738-7738-8386-37386ae03738/portal-invite" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "email": "ada.lovelace@example.com",
    "username": "string"
  }'

Responses

201
NameTypeConstraints
statusrequired
enum: "invited"—
emailrequired
string (email) | null—
usernamerequired
string | null—
invitationIdrequired
string (uuid)—
expiresAtrequired
string (date-time)—
deliveryStaterequired
enum: "queued" | "sent" | "failed"—
deliveryReasonrequired
enum: "not_enrolled" | "install_blocked" | "no_licence" | "no_key" | "unreachable" | "rate_limited" | "rejected" | "mail_failed" | "internal" | null | null—
json
{
  "status": "invited",
  "email": "ada.lovelace@example.com",
  "username": "string",
  "invitationId": "00637243-0063-7063-8637-006372430063",
  "expiresAt": "2026-03-02T09:00:00.000Z",
  "deliveryState": "queued",
  "deliveryReason": "not_enrolled"
}
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/resend

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

Requires authenticationScopestudents.write

Path & query parameters

NameInTypeConstraints
idrequired
pathstring—

curl example

curl
curl -X POST "https://your-institute.example/api/v1/students/37386ae0-3738-7738-8386-37386ae03738/portal-invite/resend" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

201
NameTypeConstraints
statusrequired
enum: "invited"—
emailrequired
string (email) | null—
usernamerequired
string | null—
invitationIdrequired
string (uuid)—
expiresAtrequired
string (date-time)—
deliveryStaterequired
enum: "queued" | "sent" | "failed"—
deliveryReasonrequired
enum: "not_enrolled" | "install_blocked" | "no_licence" | "no_key" | "unreachable" | "rate_limited" | "rejected" | "mail_failed" | "internal" | null | null—
json
{
  "status": "invited",
  "email": "ada.lovelace@example.com",
  "username": "string",
  "invitationId": "00637243-0063-7063-8637-006372430063",
  "expiresAt": "2026-03-02T09:00:00.000Z",
  "deliveryState": "queued",
  "deliveryReason": "not_enrolled"
}
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://your-institute.example/api/v1/students/37386ae0-3738-7738-8386-37386ae03738/status" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "status": "lead"
  }'

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, active groups, next session, and recent-absence count for one student.

Requires authenticationScopestudents.read

Path & query parameters

NameInTypeConstraints
idrequired
pathstring—

curl example

curl
curl -X GET "https://your-institute.example/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>—
recentAbsencesrequired
object—
nextSessionrequired
unknown | null—
assignmentsrequired
object—
quizzesrequired
object—
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
    }
  ],
  "recentAbsences": {
    "absent": 0,
    "ofLast": 0
  },
  "nextSession": "value",
  "assignments": {
    "dueSoon": 0,
    "late": 0
  },
  "quizzes": {
    "dueSoon": 0,
    "late": 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://your-institute.example/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://your-institute.example/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.