> ## Documentation Index
> Fetch the complete documentation index at: https://docs.evalezy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create an exam

> Creates an exam with its questions, and optionally registers candidates in the same call. The exam starts as a `draft`; send `open: true` to open it right away (or call [Open an exam](/api-reference/exams/open) later).

- `mode` is `handwritten` (scanned PDF copies) or `typed` (answers sent as text).
- `external_ref` is your own id for the exam. A second create with the same value returns 409 `exam_exists` with `details.exam_id`, so you can safely look it up instead.
- Long-answer questions with neither a rubric nor a model answer get an `auto_rubric` warning.
- Only English is supported (`answer_language: en`).
- `choice_groups` is beta and enabled on request; until then it returns 422 `feature_not_available`.

The response includes the exam's `quote` (credits per page or per answer) and any `warnings`. Exams created through the API also appear in your dashboard, tagged Source: API.

**Scope:** `evaluation:write`



## OpenAPI

````yaml /api-reference/openapi.json post /exams
openapi: 3.0.1
info:
  title: Evalezy AI Evaluation API
  version: v1
  description: >-
    Create exams, register candidates, submit scanned answer copies or typed
    answers, and read question-wise AI marks with feedback. Results are drafts
    until you finalize them.


    Server-to-server only: never ship an API key inside a browser or mobile app.
    English answers only for now (Hindi and regional languages are on the
    roadmap).
  contact:
    name: Evalezy
    email: hello@evalezy.com
    url: https://evalezy.com
servers:
  - url: https://api.evalezy.com/v1
    description: Production
security:
  - ApiKey: []
tags:
  - name: Exams
    description: Create, open, edit and look up exams.
  - name: Questions
    description: Add, edit and remove the questions of an exam.
  - name: Rubrics
    description: Marking criteria and model answers for long-answer questions.
  - name: Choice groups
    description: Internal choice ("attempt any N of M"). Beta, enabled on request.
  - name: Candidates
    description: >-
      Your students, keyed by your own external_id, and their exam
      registrations.
  - name: Uploads
    description: Presigned uploads for scanned answer copies (PDF).
  - name: Submissions
    description: Submit a copy or typed answers for a candidate and track its evaluation.
  - name: Results
    description: Question-wise marks, feedback and the checked (annotated) copy.
  - name: Review
    description: Teacher overrides, approval, finalize and unfinalize.
  - name: Credits
    description: Credit balance, rate card and price quotes.
  - name: Account
    description: Check which institute and scopes an API key has.
paths:
  /exams:
    post:
      tags:
        - Exams
      summary: Create an exam
      description: >-
        Creates an exam with its questions, and optionally registers candidates
        in the same call. The exam starts as a `draft`; send `open: true` to
        open it right away (or call [Open an exam](/api-reference/exams/open)
        later).


        - `mode` is `handwritten` (scanned PDF copies) or `typed` (answers sent
        as text).

        - `external_ref` is your own id for the exam. A second create with the
        same value returns 409 `exam_exists` with `details.exam_id`, so you can
        safely look it up instead.

        - Long-answer questions with neither a rubric nor a model answer get an
        `auto_rubric` warning.

        - Only English is supported (`answer_language: en`).

        - `choice_groups` is beta and enabled on request; until then it returns
        422 `feature_not_available`.


        The response includes the exam's `quote` (credits per page or per
        answer) and any `warnings`. Exams created through the API also appear in
        your dashboard, tagged Source: API.


        **Scope:** `evaluation:write`
      operationId: createExam
      parameters:
        - name: Idempotency-Key
          in: header
          required: false
          schema:
            type: string
            maxLength: 255
            example: exam-cbse-sci-10a-2026-10-14
          description: >-
            Optional. Any unique string of 1-255 printable ASCII characters (a
            UUID works). Retrying with the same key within 48 hours replays the
            first response with `Idempotent-Replayed: true`.
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateExam'
            examples:
              handwritten:
                summary: CBSE Class 10 science paper (handwritten)
                value:
                  title: Class 10 Science - Half-Yearly 2026 (Section 10-A)
                  mode: handwritten
                  external_ref: cbse-sci-10a-hy-2026
                  conducted_on: '2026-10-14'
                  subject: Science
                  level: school
                  board: CBSE
                  class: '10'
                  questions:
                    - label: Q1
                      section: A
                      type: mcq_single
                      text: Which of the following is a balanced chemical equation?
                      max_marks: 1
                      options:
                        - label: A
                          text: H2 + O2 -> H2O
                        - label: B
                          text: 2H2 + O2 -> 2H2O
                        - label: C
                          text: H2 + 2O2 -> H2O
                        - label: D
                          text: 2H2 + 2O2 -> H2O
                      correct_options:
                        - B
                    - label: Q24
                      section: C
                      type: long_answer
                      text: >-
                        Explain the process of digestion of food in the human
                        stomach. Name the enzymes involved.
                      max_marks: 3
                      expects_diagram: false
                      rubric:
                        partial_marking: true
                        criteria:
                          - name: Role of hydrochloric acid
                            marks: 1
                            guidance: Acidic medium, kills germs, activates pepsin.
                          - name: Enzymes named
                            marks: 1
                            keywords:
                              - pepsin
                              - gastric lipase
                            guidance: >-
                              Pepsin digests proteins; gastric lipase acts on
                              fats.
                          - name: Mucus and protection
                            marks: 1
                            guidance: Mucus protects the inner lining from the acid.
                  candidates:
                    - external_id: STU-10A-0142
                      name: Aarav Sharma
                      roll_number: '14'
                      section_or_class: 10-A
                  open: true
              typed:
                summary: UPSC GS2 answer writing (typed)
                value:
                  title: 'UPSC GS Paper II - Weekly Answer Writing #18'
                  mode: typed
                  external_ref: upsc-gs2-week18
                  subject: Polity and Governance
                  level: upsc
                  questions:
                    - label: Q1
                      type: long_answer
                      text: >-
                        Discuss the role of the Finance Commission in
                        strengthening fiscal federalism in India.
                      max_marks: 15
                      word_limit: 250
                      model_answer: >-
                        The Finance Commission under Article 280 recommends the
                        vertical and horizontal distribution of the divisible
                        pool of taxes...
                      rubric:
                        criteria:
                          - name: Constitutional basis
                            marks: 3
                            guidance: Article 280, composition, periodicity.
                          - name: Vertical and horizontal devolution
                            marks: 5
                            guidance: >-
                              Share of states in the divisible pool and the
                              criteria used.
                          - name: Contemporary issues
                            marks: 4
                            guidance: >-
                              Cesses and surcharges, GST compensation,
                              conditional grants.
                          - name: Structure and conclusion
                            marks: 3
                            guidance: Introduction, body, balanced way forward.
                  open: true
        required: true
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/Exam'
              example:
                id: 3f6c1d2e-8b4a-4c7e-9a51-2d0e7b9c4f18
                status: open
                mode: handwritten
                title: Class 10 Science - Half-Yearly 2026 (Section 10-A)
                external_ref: cbse-sci-10a-hy-2026
                conducted_on: '2026-10-14'
                subject: Science
                level: school
                board: CBSE
                class: '10'
                answer_language: en
                feedback_language: en
                blind: false
                total_marks: 4
                paper_max: 4
                question_count: 2
                questions:
                  - id: a1f0c2d4-6e8b-4a3c-9d5e-7f1b2c3d4e5f
                    label: Q1
                    section: A
                    max_marks: 1
                  - id: b2e1d3c5-7f9a-4b4d-8e6f-0a2c3d4e5f6a
                    label: Q24
                    section: C
                    max_marks: 3
                candidates:
                  - id: c41a7e93-2f58-4b6d-8e0a-5d9f1b3c7e62
                    external_id: STU-10A-0142
                    registration_id: e7b3f9a1-4c2d-4e8f-a6b5-0d1c9e2f7a48
                rubric:
                  version: 1
                  locked: false
                  questions_with_rubric: 1
                  questions_without_rubric: 1
                quote:
                  unit: page
                  credits_per_page: 1
                  rate_source: standard
                opened_at: '2026-10-14T05:30:12Z'
                created_at: '2026-10-14T05:30:12Z'
                updated_at: '2026-10-14T05:30:12Z'
        '400':
          $ref: '#/components/responses/Error400'
        '401':
          $ref: '#/components/responses/Error401'
        '403':
          $ref: '#/components/responses/Error403'
        '409':
          $ref: '#/components/responses/Error409'
        '422':
          $ref: '#/components/responses/Error422'
        '429':
          $ref: '#/components/responses/Error429'
components:
  schemas:
    CreateExam:
      type: object
      properties:
        title:
          type: string
          description: Exam title, up to 255 characters, no `<` or `>`.
        mode:
          type: string
          description: >-
            `handwritten` (scanned answer copies) or `typed` (answers sent as
            text).
          enum:
            - handwritten
            - typed
        external_ref:
          type: string
          description: >-
            Your own id for the exam, up to 128 characters. Unique per
            institute: a repeat returns 409 `exam_exists`.
        conducted_on:
          type: string
          description: Exam date, `YYYY-MM-DD`. Defaults to today.
          format: date
        subject:
          type: string
          description: Up to 120 characters.
        level:
          type: string
          description: '`school` (default), `ug`, `pg` or `upsc`.'
          enum:
            - school
            - ug
            - pg
            - upsc
        board:
          type: string
          description: e.g. CBSE. Up to 64 characters.
        answer_language:
          type: string
          description: Only `en` is supported. `hi` returns 422 `language_not_supported`.
          enum:
            - en
            - hi
        feedback_language:
          type: string
          description: Only `en` is supported.
          enum:
            - en
            - hi
        instructions:
          type: string
          description: Paper-level instructions for the evaluator, up to 4,000 characters.
        blind:
          type: boolean
          description: Hide candidate names from results and reviewers.
        open:
          type: boolean
          description: Open the exam in the same call so it accepts submissions right away.
        sections:
          type: array
          items:
            $ref: '#/components/schemas/SectionInput'
          description: >-
            Up to 20 sections. If omitted, sections come from the questions'
            `section` values, or a single section `A`.
        questions:
          type: array
          items:
            $ref: '#/components/schemas/QuestionInput'
          description: Up to 200 questions.
        choice_groups:
          type: array
          items:
            $ref: '#/components/schemas/ChoiceGroupInput'
          description: >-
            Beta, enabled on request. Currently returns 422
            `feature_not_available`.
        candidates:
          type: array
          items:
            $ref: '#/components/schemas/CandidateInput'
          description: >-
            Up to 2,000 candidates to create (or update) and register on the
            exam.
        class:
          type: string
          description: e.g. 10. Up to 32 characters.
    Exam:
      type: object
      properties:
        id:
          type: string
        status:
          type: string
        mode:
          type: string
        title:
          type: string
        external_ref:
          type: string
        conducted_on:
          type: string
        subject:
          type: string
        level:
          type: string
        board:
          type: string
        answer_language:
          type: string
        feedback_language:
          type: string
        instructions:
          type: string
        blind:
          type: boolean
        total_marks:
          type: number
          format: double
        paper_max:
          type: number
          format: double
        question_count:
          type: integer
          format: int32
        dashboard_url:
          type: string
        questions:
          type: array
          items:
            $ref: '#/components/schemas/Question'
        choice_groups:
          type: array
          items:
            $ref: '#/components/schemas/ChoiceGroup'
        candidates:
          type: array
          items:
            type: object
            additionalProperties:
              type: object
        rubric:
          $ref: '#/components/schemas/RubricSummary'
        quote:
          type: object
          additionalProperties:
            type: object
        stats:
          type: object
          additionalProperties:
            type: object
        warnings:
          type: array
          items:
            $ref: '#/components/schemas/Warning'
        opened_at:
          type: string
        finalized_at:
          type: string
        created_at:
          type: string
        updated_at:
          type: string
        class:
          type: string
    SectionInput:
      type: object
      properties:
        name:
          type: string
        order:
          type: integer
          format: int32
    QuestionInput:
      type: object
      properties:
        label:
          type: string
          description: >-
            Question label as printed on the paper, e.g. `Q3` or `5(b)`. Up to
            16 characters, unique in the exam.
        parent_label:
          type: string
          description: Label of the parent question for sub-parts.
        section:
          type: string
        type:
          type: string
          enum:
            - mcq_single
            - mcq_multi
            - true_false
            - numeric
            - one_word
            - long_answer
        text:
          type: string
          description: Question text, up to 20,000 characters.
        max_marks:
          type: number
          description: Greater than 0, at most 1000, in steps of 0.5.
        negative_marks:
          type: number
          description: 0 or more. Ignored (with a warning) on handwritten exams.
        options:
          type: array
          items:
            $ref: '#/components/schemas/OptionInput'
          description: 'MCQ and true/false only: 2 to 26 options.'
        correct_options:
          type: array
          items:
            type: string
          description: Option labels. Exactly one for `mcq_single` and `true_false`.
        answer:
          $ref: '#/components/schemas/JsonNode'
          description: >-
            `numeric`: a number or a list of accepted numbers. `one_word`: a
            string up to 255 characters.
        model_answer:
          type: string
          description: '`long_answer` only. Up to 20,000 characters.'
        rubric:
          $ref: '#/components/schemas/RubricInput'
          description: '`long_answer` only. Criterion marks must add up to `max_marks`.'
        word_limit:
          type: integer
          format: int32
          description: Greater than 0.
        expects_diagram:
          type: boolean
        assess_language:
          type: boolean
        tags:
          type: object
          additionalProperties:
            type: object
          description: Your own key-value tags, up to 2 KB.
        external_id:
          type: string
          description: Your id for the question, up to 128 characters.
    ChoiceGroupInput:
      type: object
      properties:
        label:
          type: string
        question_labels:
          type: array
          items:
            type: string
        attempt:
          type: integer
          format: int32
          description: >-
            How many questions of the group count. At least 1 and less than the
            group size.
        policy:
          type: string
          enum:
            - first
            - best
          description: Which answers count when a candidate attempts more than `attempt`.
    CandidateInput:
      type: object
      properties:
        external_id:
          type: string
          description: Your id for the candidate (required), up to 128 characters.
        name:
          type: string
          description: Up to 255 characters, no `<` or `>`.
        roll_number:
          type: string
          description: Up to 64 characters.
        section_or_class:
          type: string
          description: Up to 64 characters.
        metadata:
          $ref: '#/components/schemas/JsonNode'
          description: A JSON object of your own, up to 2 KB.
    Question:
      type: object
      properties:
        id:
          type: string
        label:
          type: string
        parent_label:
          type: string
        section:
          type: string
        type:
          type: string
        text:
          type: string
        max_marks:
          type: number
          format: double
        negative_marks:
          type: number
          format: double
        options:
          type: array
          items:
            $ref: '#/components/schemas/Option'
        correct_options:
          type: array
          items:
            type: string
        answer:
          type: object
        model_answer:
          type: string
        rubric:
          type: object
          additionalProperties:
            type: object
        word_limit:
          type: integer
          format: int32
        expects_diagram:
          type: boolean
        assess_language:
          type: boolean
        tags:
          type: object
          additionalProperties:
            type: object
        external_id:
          type: string
        rubric_version:
          type: integer
          format: int32
        rubric_sync:
          type: string
        warnings:
          type: array
          items:
            $ref: '#/components/schemas/Warning'
    ChoiceGroup:
      type: object
      properties:
        id:
          type: string
        label:
          type: string
        question_labels:
          type: array
          items:
            type: string
        question_ids:
          type: array
          items:
            type: string
        attempt:
          type: integer
          format: int32
        policy:
          type: string
    RubricSummary:
      type: object
      properties:
        version:
          type: integer
          format: int32
        locked:
          type: boolean
        questions_with_rubric:
          type: integer
          format: int32
        questions_without_rubric:
          type: integer
          format: int32
        sync:
          type: string
        sync_error:
          type: object
          additionalProperties:
            type: object
    Warning:
      type: object
      description: >-
        A non-fatal note about the request, e.g. `auto_rubric` or
        `pages_beyond_vision_limit`.
      properties:
        code:
          type: string
        message:
          type: string
        field:
          type: string
          nullable: true
    Error:
      type: object
      description: >-
        Every error uses this envelope. Branch on `error.code`; the message is
        for humans.
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              description: Stable machine code, e.g. `validation_failed`.
              example: validation_failed
            message:
              type: string
              description: Human-readable explanation.
              example: questions[1].max_marks must be a multiple of 0.5.
            request_id:
              type: string
              description: >-
                Quote this when you contact support (also sent as the
                X-Request-Id header).
            details:
              type: object
              description: >-
                Extra context. For `validation_failed`, `details.errors` lists
                every failing field.
              additionalProperties: true
    OptionInput:
      type: object
      properties:
        label:
          type: string
        text:
          type: string
    JsonNode:
      type: object
    RubricInput:
      type: object
      properties:
        partial_marking:
          type: boolean
          description: Allow partial marks within a criterion.
        instructions:
          type: string
          description: Up to 4,000 characters.
        criteria:
          type: array
          items:
            $ref: '#/components/schemas/CriterionInput'
          description: >-
            Up to 30 criteria. Names unique within the question; marks add up to
            the question's `max_marks`.
    Option:
      type: object
      properties:
        label:
          type: string
        option_id:
          type: string
        text:
          type: string
    CriterionInput:
      type: object
      properties:
        name:
          type: string
          description: Up to 200 characters.
        marks:
          type: number
        keywords:
          type: array
          items:
            type: string
          description: Up to 30 keywords, 100 characters each.
        guidance:
          type: string
          description: >-
            What earns this criterion, up to 2,000 characters. Must not contain
            mark figures such as "2 marks".
  responses:
    Error400:
      description: Malformed JSON or invalid cursor.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: malformed_json
              message: The request body is not valid JSON for this endpoint.
              request_id: req_4f9c2a7e1b3d4c8a9e6f0b2d5a7c1e3f
    Error401:
      description: Missing or invalid API key.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: invalid_api_key
              message: The API key is not valid.
              request_id: req_4f9c2a7e1b3d4c8a9e6f0b2d5a7c1e3f
    Error403:
      description: >-
        The key lacks the required scope, or the API is not enabled for the
        institute.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: insufficient_scope
              message: This API key lacks the evaluation:write scope.
              request_id: req_4f9c2a7e1b3d4c8a9e6f0b2d5a7c1e3f
              details:
                required_scope: evaluation:write
    Error409:
      description: Conflict with the current state.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: conflict
              message: The request conflicts with the current state.
              request_id: req_4f9c2a7e1b3d4c8a9e6f0b2d5a7c1e3f
    Error422:
      description: The request is valid JSON but breaks a rule.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: validation_failed
              message: questions[0].max_marks is required.
              request_id: req_4f9c2a7e1b3d4c8a9e6f0b2d5a7c1e3f
              details:
                errors:
                  - field: questions[0].max_marks
                    code: required
                    message: max_marks is required.
    Error429:
      description: Rate limit or daily quota reached. Retry after the Retry-After header.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            error:
              code: rate_limited
              message: Too many requests. Retry after 2 s.
              request_id: req_4f9c2a7e1b3d4c8a9e6f0b2d5a7c1e3f
  securitySchemes:
    ApiKey:
      type: apiKey
      in: header
      name: X-API-Key
      description: >-
        Your institute's API key, `vak_eval_` followed by 48 hex characters. An
        institute admin creates keys in the dashboard under Settings ->
        Integrations -> API keys.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.