---
title: "Get Class Evaluations"
method: GET
path: "/teacher/assignments/{assignment_id}/v2-evaluations"
tags: ["v2_evaluations"]
---

# Get Class Evaluations

`GET /teacher/assignments/{assignment_id}/v2-evaluations`

Class-wide evaluation snapshot. Heavy payload (~1MB for a 30×10
class), so we let the client conditionally short-circuit with
`If-None-Match` against a weak ETag derived from the latest session
`updated_at`. A teacher who keeps the results page open and re-fetches
on a route change gets a 304 with empty body when nothing has moved.

## Path parameters

- `assignment_id` string, required

## Headers

- `if-none-match` string, nullable

## Response `200`

Successful Response

- ClassEvaluationsResponse
  - `assignment_id` string, required
  - `exercises` ExerciseSummary[], required
    - `exercise_id` string, required
    - `statement` string, required
    - `rubric` RubricResponse, required — Minimal: drops `exercise_id` (lives in the URL) and `exercise_collection` / `created_at` (frontend doesn't read them today). `author` is flattened to a Literal to avoid leaking prompt ids / user ids. `scoring_scheme` is appended last and is derived, never stored — a new bundle reads it to decide which shape to render; an old one ignores it.
      - `id` string, required
      - `questions` RubricQuestionResponse[], required
        - `question_text` string, required
        - `skills` SkillRef[], required
          - `savoir_faire_id` string, required
          - `name` string, required
          - `description` string, required
        - `criteria` string[], required
        - `points` number, nullable
        - `expected_result` string, nullable
      - `author_type` 'ai' | 'human', required
      - `scoring_scheme` 'criteria' | 'points'
  - `rows` EvaluationRow[], required
    - `student_id` string, required
    - `student_display_name` string, required
    - `exercise_id` string, required
    - `latest_evaluation` RubricEvaluationResponse, required
      - `rubric_id` string, required
      - `author_type` 'ai' | 'human', required
      - `author_model` string, nullable
      - `feedback` string, required
      - `questions` RubricQuestionEvaluationResponse[], required
        - `skill_outcomes` SkillOutcome[], required
          - `savoir_faire_id` string, required
          - `outcome` 'correct' | 'partial' | 'wrong' | 'unanswered', required
          - `note` string, required
          - `evidence` EvidenceSpan[], required
            - `excerpt` string, required
            - `span` TextSpan, required — Resolved char offsets into a `SubmissionAttempt.solution_blocks[i].transcribed_text`. Backend computes start/end at write time.
              - …
        - `criterion_outcomes` CriterionOutcome[], required
          - `criterion_index` integer, required
          - `outcome` 'correct' | 'partial' | 'wrong' | 'unanswered', required
          - `note` string, required
          - `evidence` EvidenceSpan[], required
            - `excerpt` string, required
            - `span` TextSpan, required — Resolved char offsets into a `SubmissionAttempt.solution_blocks[i].transcribed_text`. Backend computes start/end at write time.
              - …
        - `errors` FlaggedError[], required
          - `description` string, required
          - `evidence` EvidenceSpan[], required
            - `excerpt` string, required
            - `span` TextSpan, required — Resolved char offsets into a `SubmissionAttempt.solution_blocks[i].transcribed_text`. Backend computes start/end at write time.
              - …
          - `categories` ErrorCategory[], nullable
        - `question_outcome` QuestionOutcomeResponse — The grader's verdict on one question of a points rubric. `max_score` travels so a client never hardcodes the ladder's top rung, and `points_awarded` / `points_available` are computed **here**, by the backend, through the one domain rule (`question_award`: the ladder, or a teacher's own mark when one was written). No client recomputes a grade from outcomes: the per-question awards sum to `computed_grade` exactly, which is what lets EXC-764 delete the frontend's duplicate weight table rather than extend it. Both point fields are `None` only when the response was built without the rubric in hand (see `RubricEvaluationResponse.from_domain`).
          - `score` integer, required
          - `max_score` integer, required
          - `unanswered` boolean, required
          - `note` string, required
          - `evidence` EvidenceSpan[], required
            - `excerpt` string, required
            - `span` TextSpan, required — Resolved char offsets into a `SubmissionAttempt.solution_blocks[i].transcribed_text`. Backend computes start/end at write time.
              - …
          - `alternative_method` boolean, required
          - `points_awarded` number, nullable, required
          - `points_available` number, nullable, required
      - `question_zones` QuestionZone[], required
        - `question_indexes` integer[], required
        - `boxes` PhotoBBox[], required
          - `photo_index` integer, required
          - `x` number, required
          - `y` number, required
          - `width` number, required
          - `height` number, required
      - `computed_grade` number, required
      - `max_grade` number, required
      - `evaluated_at` string, date-time, required
    - `submission_attempt_index` integer, nullable, required
  - `student_breakdowns` StudentSessionBreakdownResponse[], required
    - `student_id` string, required
    - `breakdown` SessionGradeBreakdownResponse, required — Canonical per-session grade payload — read by every résultats surface so the displayed note matches across teacher and student views.
      - `total` number, nullable, required
      - `cells` WeightedExerciseCellResponse[], required
        - `grade` number, nullable, required
        - `max_grade` number, required
      - `total_max` number, required
  - `legacy_submissions` LegacySubmissionRefResponse[], required
    - `session_id` string, required
    - `exercise_id` string, required

## Other responses

- `400` — Malformed identifier
- `403` — Not authorized for this resource
- `404` — Resource not found
- `422` — Validation Error

## Changes

> 19 revisions in range; 1 not diffed.

- **2026-09-22** `05e048df94c6` — 4 info
  - added the optional property `exercises/items/rubric/anyOf[subschema #1: RubricResponse]/questions/items/expected_result` to the response with the `200` status
  - added the optional property `exercises/items/rubric/anyOf[subschema #1: RubricResponse]/questions/items/points` to the response with the `200` status
  - added the optional property `exercises/items/rubric/anyOf[subschema #1: RubricResponse]/scoring_scheme` to the response with the `200` status
  - added the optional property `rows/items/latest_evaluation/anyOf[subschema #1: RubricEvaluationResponse]/questions/items/question_outcome` to the response with the `200` status
- **2026-09-07** `0ccba2913cca` — 1 info
  - added the optional property `rows/items/latest_evaluation/anyOf[subschema #1: RubricEvaluationResponse]/author_model` to the response with the `200` status
- **2026-09-03** `ef67479ed898` — 1 info
  - added the optional property `rows/items/latest_evaluation/anyOf[subschema #1: RubricEvaluationResponse]/questions/items/errors/items/categories` to the response with the `200` status

[Change history](https://skmtc.dev/excellence-ai/apis/excellence-learning/changes/teacher/assignments/:assignment_id/v2-evaluations/get.md)

---

[API](https://skmtc.dev/excellence-ai/apis/excellence-learning.md) · [All operations](https://skmtc.dev/excellence-ai/apis/excellence-learning/llms.txt) · [OpenAPI document](https://skmtc.dev/excellence-ai/apis/excellence-learning/revisions/05e048df94c6?raw)
