assessment

Assessment

Exams, recorded results, and progress reports.

15 endpoints

DELETE /api/v1/exams/{id}

Delete an exam. Only allowed when it has no results yet (409 otherwise).

Requires authenticationScopeassessment.write

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

curl example

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

Responses

200The exam was deleted.
NameTypeConstraints
okrequired
enum: true
json
{
  "ok": 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.

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/exams/{id}

Update an exam.

Requires authenticationScopeassessment.write

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

Request body

NameTypeConstraints
title
string1–200 chars
kind
enum: "quiz" | "exam" | "homework" | "project" | "final"
maxScore
number≥0
weight
number≥0 · default: 1
heldAt
string | string (date)

Example

json
{
  "title": "Midterm Exam",
  "kind": "quiz",
  "maxScore": 0,
  "weight": 1,
  "heldAt": "string"
}

curl example

curl
curl -X PATCH "https://api.yourdomain.com/api/v1/exams/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "title": "Midterm Exam",
    "kind": "quiz",
    "maxScore": 0,
    "weight": 1,
    "heldAt": "string"
  }'

Responses

200
NameTypeConstraints
idrequired
string (uuid)
groupIdrequired
string (uuid)
titlerequired
string
kindrequired
enum: "quiz" | "exam" | "homework" | "project" | "final"
maxScorerequired
number
weightrequired
number
heldAtrequired
string (date) | null
publishedAtrequired
string (date-time) | null
createdAtrequired
string (date-time)
updatedAtrequired
string (date-time)
json
{
  "id": "37386ae0-3738-7738-8386-37386ae03738",
  "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
  "title": "Midterm Exam",
  "kind": "quiz",
  "maxScore": 1,
  "weight": 1,
  "heldAt": "2026-03-02",
  "publishedAt": "2026-03-02T09:00:00.000Z",
  "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.

POST /api/v1/exams/{id}/publish

Publish an exam (once only — 409 on re-publish). Emits `exam.published`.

Requires authenticationScopeassessment.write

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

curl example

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

Responses

200
NameTypeConstraints
idrequired
string (uuid)
groupIdrequired
string (uuid)
titlerequired
string
kindrequired
enum: "quiz" | "exam" | "homework" | "project" | "final"
maxScorerequired
number
weightrequired
number
heldAtrequired
string (date) | null
publishedAtrequired
string (date-time) | null
createdAtrequired
string (date-time)
updatedAtrequired
string (date-time)
json
{
  "id": "37386ae0-3738-7738-8386-37386ae03738",
  "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
  "title": "Midterm Exam",
  "kind": "quiz",
  "maxScore": 1,
  "weight": 1,
  "heldAt": "2026-03-02",
  "publishedAt": "2026-03-02T09:00:00.000Z",
  "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.

PUT /api/v1/exams/{id}/results

Bulk upsert an exam's results roster.

Requires authenticationScopeassessment.write

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

Request body

NameTypeConstraints
resultsrequired
array<object>

Example

json
{
  "results": [
    {
      "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
      "score": 0,
      "isAbsent": false,
      "remarks": "string"
    }
  ]
}

curl example

curl
curl -X PUT "https://api.yourdomain.com/api/v1/exams/37386ae0-3738-7738-8386-37386ae03738/results" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "results": [
      {
        "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
        "score": 0,
        "isAbsent": false,
        "remarks": "string"
      }
    ]
  }'

Responses

200
NameTypeConstraints
datarequired
array<object>
json
{
  "data": [
    {
      "id": "37386ae0-3738-7738-8386-37386ae03738",
      "examId": "6c140414-6c14-7c14-8140-6c1404146c14",
      "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
      "score": 0,
      "isAbsent": true,
      "remarks": "string",
      "gradedByUserId": "71e16c27-71e1-71e1-8e16-71e16c2771e1",
      "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/groups/{groupId}/exams

List a group's exams.

Requires authenticationScopeassessment.read

Path & query parameters

NameInTypeConstraints
groupIdrequired
pathstring
cursor
querystring
limit
queryinteger1–100 · default: 25

curl example

curl
curl -X GET "https://api.yourdomain.com/api/v1/groups/6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7/exams" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY"

Responses

200
NameTypeConstraints
datarequired
array<object>
nextCursorrequired
string | null
json
{
  "data": [
    {
      "id": "37386ae0-3738-7738-8386-37386ae03738",
      "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
      "title": "Midterm Exam",
      "kind": "quiz",
      "maxScore": 1,
      "weight": 1,
      "heldAt": "2026-03-02",
      "publishedAt": "2026-03-02T09:00:00.000Z",
      "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/groups/{groupId}/exams

Create an exam for this group.

Requires authenticationScopeassessment.write

Path & query parameters

NameInTypeConstraints
groupIdrequired
pathstring

Request body

NameTypeConstraints
titlerequired
string1–200 chars
kindrequired
enum: "quiz" | "exam" | "homework" | "project" | "final"
maxScorerequired
number≥0
weight
number≥0 · default: 1
heldAt
string | string (date)

Example

json
{
  "title": "Midterm Exam",
  "kind": "quiz",
  "maxScore": 0,
  "weight": 1,
  "heldAt": "string"
}

curl example

curl
curl -X POST "https://api.yourdomain.com/api/v1/groups/6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7/exams" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "title": "Midterm Exam",
    "kind": "quiz",
    "maxScore": 0,
    "weight": 1,
    "heldAt": "string"
  }'

Responses

201
NameTypeConstraints
idrequired
string (uuid)
groupIdrequired
string (uuid)
titlerequired
string
kindrequired
enum: "quiz" | "exam" | "homework" | "project" | "final"
maxScorerequired
number
weightrequired
number
heldAtrequired
string (date) | null
publishedAtrequired
string (date-time) | null
createdAtrequired
string (date-time)
updatedAtrequired
string (date-time)
json
{
  "id": "37386ae0-3738-7738-8386-37386ae03738",
  "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
  "title": "Midterm Exam",
  "kind": "quiz",
  "maxScore": 1,
  "weight": 1,
  "heldAt": "2026-03-02",
  "publishedAt": "2026-03-02T09:00:00.000Z",
  "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/me/grades

Own grades (student), or a linked child’s grades via `?studentId` (guardian). Published exams only.

Requires authenticationScopeNo additional permission

Path & query parameters

NameInTypeConstraints
studentId
querystring (uuid)

curl example

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

Responses

200
NameTypeConstraints
studentIdrequired
string (uuid)
groupsrequired
array<object>
json
{
  "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
  "groups": [
    {
      "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
      "groupName": "string",
      "weightedAveragePercentage": 0,
      "grade": 1,
      "scaleUnsupported": true,
      "exams": [
        {
          "examId": "6c140414-6c14-7c14-8140-6c1404146c14",
          "title": "Midterm Exam",
          "kind": "quiz",
          "maxScore": 1,
          "weight": 1,
          "score": 0,
          "isAbsent": true,
          "percentage": 0
        }
      ]
    }
  ]
}
400The request could not be read at all — unparseable body, unusable content type, oversized payload (`MALFORMED_REQUEST`). Never a schema failure: those are 422.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

GET /api/v1/me/progress-reports

My own (or my children’s) published progress reports, newest first. Drafts are never returned.

Requires authenticationScopeNo additional permission

Path & query parameters

NameInTypeConstraints
cursor
querystring
limit
queryinteger1–100 · default: 25
studentId
querystring (uuid)

curl example

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

Responses

200
NameTypeConstraints
datarequired
array<object>
nextCursorrequired
string | null
json
{
  "data": [
    {
      "id": "37386ae0-3738-7738-8386-37386ae03738",
      "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
      "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
      "periodStart": "2026-03-02",
      "periodEnd": "2026-03-02",
      "summary": {
        "attendance": {
          "totalHeld": 0,
          "present": 0,
          "late": 0,
          "absent": 0,
          "excused": 0,
          "unmarked": 0,
          "ratePercentage": 0
        },
        "weightedAveragePercentage": 0,
        "exams": [
          {
            "examId": "6c140414-6c14-7c14-8140-6c1404146c14",
            "title": "Midterm Exam",
            "kind": "quiz",
            "maxScore": 1,
            "weight": 1,
            "score": 0,
            "isAbsent": true,
            "percentage": 0
          }
        ]
      },
      "teacherComment": "string",
      "status": "draft",
      "generatedAt": "2026-03-02T09:00:00.000Z",
      "sentAt": "2026-03-02T09:00:00.000Z",
      "fileId": "0daca32e-0dac-7dac-8aca-0daca32e0dac",
      "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.

GET /api/v1/progress-reports

List progress reports, filterable by studentId, groupId, and/or status.

Requires authenticationScopeassessment.read

Path & query parameters

NameInTypeConstraints
cursor
querystring
limit
queryinteger1–100 · default: 25
studentId
querystring (uuid)
groupId
querystring (uuid)
status
queryenum: "draft" | "published" | "sent"

curl example

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

Responses

200
NameTypeConstraints
datarequired
array<object>
nextCursorrequired
string | null
json
{
  "data": [
    {
      "id": "37386ae0-3738-7738-8386-37386ae03738",
      "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
      "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
      "periodStart": "2026-03-02",
      "periodEnd": "2026-03-02",
      "summary": {
        "attendance": {
          "totalHeld": 0,
          "present": 0,
          "late": 0,
          "absent": 0,
          "excused": 0,
          "unmarked": 0,
          "ratePercentage": 0
        },
        "weightedAveragePercentage": 0,
        "exams": [
          {
            "examId": "6c140414-6c14-7c14-8140-6c1404146c14",
            "title": "Midterm Exam",
            "kind": "quiz",
            "maxScore": 1,
            "weight": 1,
            "score": 0,
            "isAbsent": true,
            "percentage": 0
          }
        ]
      },
      "teacherComment": "string",
      "status": "draft",
      "generatedAt": "2026-03-02T09:00:00.000Z",
      "sentAt": "2026-03-02T09:00:00.000Z",
      "fileId": "0daca32e-0dac-7dac-8aca-0daca32e0dac",
      "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.

GET /api/v1/progress-reports/{id}

Get one progress report.

Requires authenticationScopeassessment.read

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

curl example

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

Responses

200
NameTypeConstraints
idrequired
string (uuid)
studentIdrequired
string (uuid)
groupIdrequired
string (uuid) | null
periodStartrequired
string (date)
periodEndrequired
string (date)
summaryrequired
object
teacherCommentrequired
string | null0–2000 chars
statusrequired
enum: "draft" | "published" | "sent"
generatedAtrequired
string (date-time)
sentAtrequired
string (date-time) | null
fileIdrequired
string (uuid) | null
createdAtrequired
string (date-time)
updatedAtrequired
string (date-time)
json
{
  "id": "37386ae0-3738-7738-8386-37386ae03738",
  "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
  "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
  "periodStart": "2026-03-02",
  "periodEnd": "2026-03-02",
  "summary": {
    "attendance": {
      "totalHeld": 0,
      "present": 0,
      "late": 0,
      "absent": 0,
      "excused": 0,
      "unmarked": 0,
      "ratePercentage": 0
    },
    "weightedAveragePercentage": 0,
    "exams": [
      {
        "examId": "6c140414-6c14-7c14-8140-6c1404146c14",
        "title": "Midterm Exam",
        "kind": "quiz",
        "maxScore": 1,
        "weight": 1,
        "score": 0,
        "isAbsent": true,
        "percentage": 0
      }
    ]
  },
  "teacherComment": "string",
  "status": "draft",
  "generatedAt": "2026-03-02T09:00:00.000Z",
  "sentAt": "2026-03-02T09:00:00.000Z",
  "fileId": "0daca32e-0dac-7dac-8aca-0daca32e0dac",
  "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/progress-reports/{id}

Set a progress report's teacher comment.

Requires authenticationScopeassessment.write

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

Request body

NameTypeConstraints
teacherCommentrequired
string1–2000 chars

Example

json
{
  "teacherComment": "string"
}

curl example

curl
curl -X PATCH "https://api.yourdomain.com/api/v1/progress-reports/37386ae0-3738-7738-8386-37386ae03738" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "teacherComment": "string"
  }'

Responses

200
NameTypeConstraints
idrequired
string (uuid)
studentIdrequired
string (uuid)
groupIdrequired
string (uuid) | null
periodStartrequired
string (date)
periodEndrequired
string (date)
summaryrequired
object
teacherCommentrequired
string | null0–2000 chars
statusrequired
enum: "draft" | "published" | "sent"
generatedAtrequired
string (date-time)
sentAtrequired
string (date-time) | null
fileIdrequired
string (uuid) | null
createdAtrequired
string (date-time)
updatedAtrequired
string (date-time)
json
{
  "id": "37386ae0-3738-7738-8386-37386ae03738",
  "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
  "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
  "periodStart": "2026-03-02",
  "periodEnd": "2026-03-02",
  "summary": {
    "attendance": {
      "totalHeld": 0,
      "present": 0,
      "late": 0,
      "absent": 0,
      "excused": 0,
      "unmarked": 0,
      "ratePercentage": 0
    },
    "weightedAveragePercentage": 0,
    "exams": [
      {
        "examId": "6c140414-6c14-7c14-8140-6c1404146c14",
        "title": "Midterm Exam",
        "kind": "quiz",
        "maxScore": 1,
        "weight": 1,
        "score": 0,
        "isAbsent": true,
        "percentage": 0
      }
    ]
  },
  "teacherComment": "string",
  "status": "draft",
  "generatedAt": "2026-03-02T09:00:00.000Z",
  "sentAt": "2026-03-02T09:00:00.000Z",
  "fileId": "0daca32e-0dac-7dac-8aca-0daca32e0dac",
  "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.

POST /api/v1/progress-reports/{id}/publish

Publish a draft progress report (409 if not currently draft).

Requires authenticationScopeassessment.write

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

curl example

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

Responses

200
NameTypeConstraints
idrequired
string (uuid)
studentIdrequired
string (uuid)
groupIdrequired
string (uuid) | null
periodStartrequired
string (date)
periodEndrequired
string (date)
summaryrequired
object
teacherCommentrequired
string | null0–2000 chars
statusrequired
enum: "draft" | "published" | "sent"
generatedAtrequired
string (date-time)
sentAtrequired
string (date-time) | null
fileIdrequired
string (uuid) | null
createdAtrequired
string (date-time)
updatedAtrequired
string (date-time)
json
{
  "id": "37386ae0-3738-7738-8386-37386ae03738",
  "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
  "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
  "periodStart": "2026-03-02",
  "periodEnd": "2026-03-02",
  "summary": {
    "attendance": {
      "totalHeld": 0,
      "present": 0,
      "late": 0,
      "absent": 0,
      "excused": 0,
      "unmarked": 0,
      "ratePercentage": 0
    },
    "weightedAveragePercentage": 0,
    "exams": [
      {
        "examId": "6c140414-6c14-7c14-8140-6c1404146c14",
        "title": "Midterm Exam",
        "kind": "quiz",
        "maxScore": 1,
        "weight": 1,
        "score": 0,
        "isAbsent": true,
        "percentage": 0
      }
    ]
  },
  "teacherComment": "string",
  "status": "draft",
  "generatedAt": "2026-03-02T09:00:00.000Z",
  "sentAt": "2026-03-02T09:00:00.000Z",
  "fileId": "0daca32e-0dac-7dac-8aca-0daca32e0dac",
  "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.

POST /api/v1/progress-reports/{id}/send

Send a published progress report to its guardians (409 unless currently published). Emits `progress_report.sent`.

Requires authenticationScopeassessment.write

Path & query parameters

NameInTypeConstraints
idrequired
pathstring

curl example

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

Responses

200
NameTypeConstraints
idrequired
string (uuid)
studentIdrequired
string (uuid)
groupIdrequired
string (uuid) | null
periodStartrequired
string (date)
periodEndrequired
string (date)
summaryrequired
object
teacherCommentrequired
string | null0–2000 chars
statusrequired
enum: "draft" | "published" | "sent"
generatedAtrequired
string (date-time)
sentAtrequired
string (date-time) | null
fileIdrequired
string (uuid) | null
createdAtrequired
string (date-time)
updatedAtrequired
string (date-time)
json
{
  "id": "37386ae0-3738-7738-8386-37386ae03738",
  "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
  "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
  "periodStart": "2026-03-02",
  "periodEnd": "2026-03-02",
  "summary": {
    "attendance": {
      "totalHeld": 0,
      "present": 0,
      "late": 0,
      "absent": 0,
      "excused": 0,
      "unmarked": 0,
      "ratePercentage": 0
    },
    "weightedAveragePercentage": 0,
    "exams": [
      {
        "examId": "6c140414-6c14-7c14-8140-6c1404146c14",
        "title": "Midterm Exam",
        "kind": "quiz",
        "maxScore": 1,
        "weight": 1,
        "score": 0,
        "isAbsent": true,
        "percentage": 0
      }
    ]
  },
  "teacherComment": "string",
  "status": "draft",
  "generatedAt": "2026-03-02T09:00:00.000Z",
  "sentAt": "2026-03-02T09:00:00.000Z",
  "fileId": "0daca32e-0dac-7dac-8aca-0daca32e0dac",
  "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.

POST /api/v1/progress-reports/generate

Enqueue progress-report generation for a group (one draft per enrolled student) or a single student.

Requires authenticationScopeassessment.write

Request body

NameTypeConstraints
groupId
string (uuid)
studentId
string (uuid)
periodStartrequired
string (date)
periodEndrequired
string (date)

Example

json
{
  "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
  "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
  "periodStart": "2026-03-02",
  "periodEnd": "2026-03-02"
}

curl example

curl
curl -X POST "https://api.yourdomain.com/api/v1/progress-reports/generate" \
  -H "Authorization: Bearer $INSTITFLOW_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
    "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
    "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
    "periodStart": "2026-03-02",
    "periodEnd": "2026-03-02"
  }'

Responses

202
NameTypeConstraints
queuedrequired
enum: true
json
{
  "queued": 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.

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}/grades

Per-exam scores and per-group weighted average (scaled via `grading.scale`) for one student.

Requires authenticationScopeassessment.read

Path & query parameters

NameInTypeConstraints
idrequired
pathstring
groupId
querystring (uuid)

curl example

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

Responses

200
NameTypeConstraints
studentIdrequired
string (uuid)
groupsrequired
array<object>
json
{
  "studentId": "a4a70332-a4a7-74a7-8a70-a4a70332a4a7",
  "groups": [
    {
      "groupId": "6dc7fce6-6dc7-7dc7-8c7f-6dc7fce66dc7",
      "groupName": "string",
      "weightedAveragePercentage": 0,
      "grade": 1,
      "scaleUnsupported": true,
      "exams": [
        {
          "examId": "6c140414-6c14-7c14-8140-6c1404146c14",
          "title": "Midterm Exam",
          "kind": "quiz",
          "maxScore": 1,
          "weight": 1,
          "score": 0,
          "isAbsent": true,
          "percentage": 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.