Grades
The grading system uses a draft/published model. Teachers edit draft grades freely without affecting what students see; a grade becomes visible to the student only after it is explicitly published.
All grade calculations (lesson averages, module averages) are computed server-side based on assignment weights. Callers never write calculated fields directly.
Grade Row Entity
A grade row represents one student's grade for one assignment. It carries both a draft and a published copy:
{
"assignment_id": "asgn_001",
"student_user_id": "usr_456",
"draft": {
"numeric_grade": 8.5,
"is_exempt": false,
"feedback": "Good work overall, but missing edge case handling.",
"timestamp": "2026-03-20T10:00:00Z"
},
"published": {
"numeric_grade": 8.5,
"is_exempt": false,
"feedback": "Good work overall, but missing edge case handling.",
"timestamp": "2026-03-21T09:00:00Z"
}
}
Grade field notes:
numeric_grade— decimal value from0to10.nullif not yet graded.is_exempt— whentrue, this assignment is excluded from the student's grade average.feedback— free-text feedback string.nullif none provided.
Visibility rules:
- Teachers and Admins receive both
draftandpublishedobjects. - Students receive only the
publishedobject;draftis omitted from their response.
Lesson Grade Summary
Aggregates all assignment grades for a student within a lesson. Calculated server-side as a weighted average of published (or draft) assignment grades.
{
"lesson_id": "les_456",
"student_user_id": "usr_456",
"draft_overall_grade": 8.2,
"published_overall_grade": 8.0
}
Students receive only published_overall_grade; draft_overall_grade is omitted.
Module Grade Summary
Aggregates lesson grades within a module. Calculated server-side as a simple average of lesson overall grades.
{
"module_id": "mod_123",
"student_user_id": "usr_456",
"draft_overall_grade": 7.9,
"published_overall_grade": 7.5
}
Endpoints
GET /api/v1/lessons/{lessonId}/grades
Retrieve the grade table for a lesson.
Authentication: Required.
Behavior by role:
- TEACHER / ADMIN — returns all students' grade rows (draft + published).
- STUDENT — returns only the calling student's own grade row (published values only).
Response: Array of grade row objects, each keyed by assignment_id and student_user_id.
PATCH /api/v1/grades/{assignmentId}/{studentId}
Create or update the draft grade for one student on one assignment. Changes are saved immediately as draft and do not affect what the student sees until published.
Authentication: Required — class TEACHER or global ADMIN.
Request:
{
"numeric_grade": 8.5,
"is_exempt": false,
"feedback": "Good work overall, but missing edge case handling."
}
All fields are optional; only provided fields are updated.
Response: Updated grade row object.
POST /api/v1/grades/{assignmentId}/{studentId}/publish
Publish the current draft grade, copying draft values into published. The student can now see their grade.
Authentication: Required — class TEACHER or global ADMIN.
Precondition: A draft grade must exist (i.e. draft.numeric_grade is not null, or draft.is_exempt is true). Returns PRECONDITION_FAILED otherwise.
Request body: Empty.
Response: Updated grade row with both draft and published populated.
POST /api/v1/grades/{assignmentId}/{studentId}/unpublish
Retract a published grade, clearing the published object. The student can no longer see this grade until it is re-published.
Authentication: Required — class TEACHER or global ADMIN.
Request body: Empty.
Response: Updated grade row with published: null.
GET /api/v1/modules/{moduleId}/grade-summary
Retrieve the module-level grade aggregate for the current user (student) or a specific student (teacher/admin).
Authentication: Required — approved class member or global ADMIN.
Query parameters (TEACHER/ADMIN only):
| Parameter | Description |
|---|---|
student_id | Return grades for this specific student. Omit to get all students. |
Response:
- For a STUDENT caller: single module grade summary object (published values only).
- For a TEACHER / ADMIN caller: array of module grade summary objects for all students (draft + published).