Skip to main content
An exam is one question paper sat by a group of candidates: a CBSE Class X Science half-yearly, a B.Com Semester III booklet, or a single UPSC GS2 question set for today’s batch. It holds the paper’s questions, their marks and answer keys, and the settings the grader needs. Every exam you create through the API also appears in the institute’s Vacademy dashboard, tagged Source: API, so teachers can open it and review copies there. The exam’s dashboard_url links straight to it. API keys only see exams created through the API. Exams that teachers created in the dashboard are not visible to the API.

Lifecycle

finalized is worked out from the exam’s submissions; you don’t finalize an exam directly. POST /exams/{id}/finalize finalizes submissions, and the exam shows finalized only once all of its live submissions are final. Finalizing some of them leaves the exam open. A new submission, or unfinalizing one, makes the exam open again. Finalizing changes state only: it publishes nothing and sends nothing to students. See Finalize.
For a one-off paper such as a daily UPSC answer-writing question, pass "open": true on create. The exam is checked and opened in the same call, and you can submit straight away.

Create an exam

POST /exams (scope evaluation:write) creates the exam in draft. You can send its questions and candidates in the same call, or add them later.
The 201 response is the exam, plus a compact list of its questions with the ids you will need later:
quote is the price of the exam’s billing unit (a page for handwritten exams, an answer for typed ones). It is left out if the price could not be read in time. Creating the exam never fails because of it. If you sent rubrics or model answers, rubric.version shows the rubric version once they are saved. If saving is delayed, rubric shows "sync": "pending" instead, and delivery is retried automatically. See Sync status.

Exam fields

string
required
Up to 255 characters. Cannot contain < or >.
string
required
handwritten (you upload scanned answer booklets as PDFs) or typed (you send each candidate’s answers as text). Can be changed only while the exam is a draft.
string
Your own id for the exam, up to 128 characters. Unique per institute: a second exam with the same value returns 409 exam_exists with the existing exam’s id in details.exam_id. Deleting an exam frees its external_ref for reuse.
string
default:"today"
The date the paper was sat, as YYYY-MM-DD.
string
Up to 120 characters, for example Science or Financial Accounting. Sent to the grader as context.
string
default:"school"
One of school, ug, pg, upsc. Sent to the grader as context.
string
Up to 64 characters, for example CBSE.
string
Up to 32 characters, for example 10.
string
Examiner instructions for the whole paper, up to 4,000 characters. Sent to the grader as context.
string
default:"en"
Only en is supported today. hi returns 422 language_not_supported.
string
default:"en"
The language of the grader’s feedback. Only en is supported today. hi returns 422 language_not_supported.
boolean
default:"false"
When true, candidate names are left out: candidates are registered under their external_id, and results return name: null. Can be changed only while the exam is a draft.
boolean
default:"false"
When true, the open checks run in the same call and the exam is created already open. If a check fails, nothing is created and you get 422 exam_not_ready.
object[]
Up to 20 sections, each {"name": "A", "order": 1}. Names are up to 64 characters, unique ignoring case, and cannot contain < or >. If you declare sections, every question must name one of them. If you don’t, sections are created from the questions’ section values in the order they first appear, or a single section A.
object[]
Up to 200 questions. See Questions.
object[]
Up to 2,000 candidates, created or updated and then registered on the exam. See Candidates.
object[]
Internal choice (“attempt any N of M”). Beta, enabled on request; see Internal choice.

Questions

Each question carries its printed label, its type, its marks and, depending on the type, an answer key, a model answer or a rubric.

Question types

How each type is marked depends on the exam’s mode:
  • Typed exams. Objective questions (mcq_single, mcq_multi, true_false, numeric, one_word) are marked against your answer key. Only long_answer questions go to the AI.
  • Handwritten exams. The AI reads the scanned booklet and marks every question, including MCQs.

Question fields

string
required
The number printed on the paper, up to 16 characters: 1, 3(a), Q11, 33-OR. Unique within the exam, ignoring case. Cannot contain < or >. Results come back keyed by both label and question id.
string
The label of the parent question, for sub-parts. For example, 3 for 3(a). Stored and returned.
string
The section name. If left out, the question goes to the first section.
string
required
One of the question types. It cannot be changed later: delete the question and add it again.
string
required
The question as printed, as plain text up to 20,000 characters.
number
required
Greater than 0, at most 1000, in steps of 0.5 (1, 2.5, 15).
number
default:"0"
Marks deducted for a wrong answer, 0 or more. Use it on objective questions in typed exams. Handwritten exams don’t apply negative marking: the value is set to 0 and the response carries a negative_marks_ignored warning.
object[]
For mcq_single, mcq_multi and true_false only: 2 to 26 options, each {"label": "A", "text": "..."}. Labels are your own (A-D, 1-4, i-iv), up to 16 characters, unique, and cannot contain < or >. Option text is up to 2,000 characters.
string[]
The labels of the correct options. Each must match one of options, or the request fails with 422 unknown_option_label.
number | string | array
For numeric: a number, a numeric string, or a list of accepted numbers (for example [0.5, 0.50]). For one_word: a string. Not allowed on other types.
string
For long_answer only. The answer an examiner would accept, up to 20,000 characters. See Rubrics and model answers.
object
For long_answer only. Criteria with marks that add up to max_marks. See Rubrics and model answers.
integer
Greater than 0. Stored and returned with the question.
boolean
Marks a question that asks for a diagram. Stored and returned with the question.
boolean
Marks a language-paper question. Stored and returned with the question.
object
Any JSON object up to 2 KB, such as {"co": "CO2", "bloom": "apply", "topic": "Respiration"}. Stored and returned.
string
Your own id for the question, up to 128 characters.
All text you send is plain text. Markup is stored and shown literally, never rendered. Short identifiers (title, question and option labels, section names, one_word answers, criterion names, candidate names and roll numbers) cannot contain < or >. Longer text (question text, option text, instructions, model answers, guidance) can. A NUL character anywhere in a request body is refused with 422 validation_failed (field code invalid_characters).

Examples by type

Manage questions

PATCH merges your fields onto the question and checks the result with the same rules as create. In a draft you can change any field except type and section; to change those, delete the question and add it again. Unknown fields are refused with 422 validation_failed (field code unknown_field), so a typo never looks like a successful edit.

Open an exam

POST /exams/{id}/open moves a draft to open, which means it can accept submissions. It runs these checks first:
  • The exam has at least one question.
  • Every question has max_marks.
  • Every rubric’s criteria add up to its question’s max_marks.
If any check fails, you get 422 exam_not_ready with every problem listed:
Problem codes are no_questions, max_marks_missing and rubric_marks_mismatch. Opening an exam that is already open changes nothing and returns 200. Opening a finalized exam returns 409 exam_finalized.

What is frozen after open

Copies are marked against the scheme as it stood when the exam opened, so after open: A 409 exam_open response lists the refused fields in details.fields. While the exam is finalized, every change to it is refused with 409 exam_finalized; new submissions are still accepted and reopen it.

Read, list and find exams

  • GET /exams/{id} returns the exam. Add ?include= with any of questions, candidates (the first 200 registrations), choice_groups, stats and rubric.
  • GET /exams lists the institute’s API exams, including those created by its other keys. It takes status (draft, open, finalized or deleted), updated_since, cursor and limit (1 to 200, default 50).
  • POST /exams/search with {"external_refs": ["ERP-EXAM-88213"]} (1 to 500 values) finds exams by your own ids. Deleted exams are left out. The lookup takes a request body rather than a query string, so your ids stay out of URLs and access logs.
include=stats returns counts for the exam:

Edit an exam

PATCH /exams/{id} takes any of the editable fields:
  • Any time before finalize: title, external_ref, conducted_on, subject, board, class, level, instructions, feedback_language.
  • Draft only: sections, blind, mode.
  • Never: answer_language and status. These return 422 validation_failed with field code not_editable.
conducted_on cannot be cleared. Sending sections replaces the list: sections are matched by name, and a section you leave out is removed, unless it still has questions (field code section_not_empty). Switching a draft to handwritten sets any negative marks to 0 and returns a negative_marks_ignored warning.

Delete an exam

DELETE /exams/{id} deletes a draft at any time. You can also delete an open exam, as long as it has no submissions; otherwise you get 409 exam_has_submissions. Deletion frees the exam’s external_ref. ?purge=true (which would also erase the files) is not available yet and returns 422 feature_not_available.

Warnings

A successful response can carry warnings: [{"code", "message", "field"}] for input that was accepted but deserves a second look:

Internal choice (beta)

Many papers offer a choice: “attempt any 5 of 8” or “33 OR 33-OR”. Choice groups tell the grader which questions count, and set paper_max to match:
Choice groups are in beta and enabled on request. They are currently switched off, so choice_groups on POST /exams and PUT /exams/{id}/choice-groups return 422 feature_not_available, and every alternative counts towards the total. For now, send papers without internal choice, or contact us if you need choice groups: hello@evalezy.com.
Once choice groups are switched on, the rules are: each group lists at least two question labels, a question can belong to only one group, attempt is at least 1 and fewer than the group’s questions, and policy is first (the first questions attempted, in answer order) or best (the highest-marked). paper_max is the marks of every question outside a group, plus the top attempt maxima of each group.

Errors