Skip to main content
Objective questions have an answer key. A long_answer question needs something more: a rubric (named criteria, each worth part of the marks) or a model answer (the answer an examiner would accept), or both. Rubrics and model answers apply to long_answer questions in both handwritten and typed exams.
Send a rubric or a model answer for every long-answer question before the first submission. It is the single biggest factor in marking quality and consistency.

The rubric object

This is a rubric for a 15-mark question: its criteria add up to 2 + 6 + 3 + 2 + 2 = 15.
boolean
default:"true"
Whether a criterion can earn part of its marks.
string
Instructions for this question, up to 4,000 characters.
object[]
required
1 to 30 criteria.

Validation rules

Rubrics are checked when you send them, so a wrong rubric never silently changes how a paper is marked.

Keep marks out of guidance

Marks belong in criteria[].marks only. If guidance also mentions marks, the grader gets two sources of truth that can disagree. The check refuses:
  • A number followed by mark, marks, mk, point, pts and similar: “2 marks”, “1.5 points”.
  • A number word followed by mark(s): “half marks”, “full marks”, “two marks”.
  • An awarding verb (award, give, deduct, allot, allocate, assign, grant, cut) followed by an amount: “award 1”, “give half”, “deduct one”.
  • The signs ½, ¼ and ¾.
Bare numbers are fine, so “Article 356”, “Section 80C”, “the 1994 judgment” and “two dimensions” all pass.
Phrases like “give full credit” also match the check, because “give” is followed by “full”. Write “credit if the student names glucose” instead.

Model answers

A model answer is plain text, up to 20,000 characters: the answer an examiner would accept, with the points that earn marks. You can send it on its own or alongside a rubric.

Without a rubric

A generated rubric is consistent, but nobody has reviewed it. Read it with GET /exams/{id}/rubrics after the first copy, and replace it with your own if needed.

Set rubrics and model answers

You can send rubrics and model answers inline with each question on POST /exams or POST /exams/{id}/questions. To change them later, use one of the endpoints below. They work in draft and after open, until the exam is finalized.
PUT /exams/{id}/questions/{question_id}/rubric
Response:
Each question’s change is {"rubric": {...} | null, "model_answer": "..." | null}:
  • Key left out: that part is left as it is.
  • null: that part is deleted.
  • Value: that part is replaced.
At least one of the two keys is required. Each rubric is checked against its question’s current max_marks. You can also set or clear a rubric with PATCH /exams/{id}/questions/{question_id} and a rubric or model_answer field.

Versions and If-Match

Every accepted change bumps the exam’s rubric version. Each result records the rubric_version it was graded with, so you can tell which results were graded before a change. To avoid overwriting someone else’s edit (for example, a teacher editing the same rubric in the dashboard), send If-Match: <version> with the version you last read:
  1. Read the current version with GET /exams/{id}/rubrics.
  2. Send your change with If-Match: 4.
  3. If the rubric is no longer at version 4, you get 412 rubric_version_mismatch with details.expected and details.current. Re-read, merge and retry.
If-Match takes a plain number (4, "4" and W/"4" are all accepted). Anything else returns 422 validation_failed. An exam with no stored rubric yet is at version 0.

Sync status

Changes are saved straight away and then delivered to the grader. Usually that takes a moment and the response carries the new version. If delivery is delayed, the response carries "version": null, "sync": "pending", and delivery is retried automatically every minute. While changes are pending, a write with If-Match returns 412 rubric_version_mismatch, because the current version isn’t known yet. Retry shortly. If the grader refuses a change that was already accepted, GET /exams/{id}/rubrics shows "sync": "failed" and a sync_error object listing the affected question_ids. Set those questions’ rubrics again to clear it.

Read rubrics

GET /exams/{id}/rubrics (scope evaluation:read) returns every question’s rubric and model answer, with changes that are still syncing already applied:
source is partner (you set it), generated (created from the question text) or none. If the grader is briefly unreachable, this call returns 503 engine_unavailable with Retry-After: 30.
Rubric generation on request and rubric locking are on the roadmap. Until then, write rubrics yourself, or let the first copy generate them and review them afterwards.

Errors