---
title: "Create quizzes"
method: POST
path: "/v2/quizzes/"
tags: ["Quizzes"]
---

# Create quizzes

`POST /v2/quizzes/`

Create up to BATCH_MAX_ITEMS quizzes in a single request.

The ``data`` array must contain at least one item and at most
``BATCH_MAX_ITEMS`` items. Each item is processed independently — a failure
on one row does not abort the rest. Response is 200 if all items succeed,
207 if any fail, 400 if the request envelope is empty or over the cap.

Per-item isolation applies only *after* the envelope is parsed. Any
schema-level validation error (422) — a malformed field or a cross-field
invariant violation (``check_quiz_cross_field_rules``) on any single item —
is raised by Pydantic before this function runs and rejects the whole
request. Only failures surfaced by the service inside the loop are isolated
per item.

Unlike lessons, ``Quiz`` has no parent FK, so there is no ``not_found``
per-item path on create — a schema-valid item always persists unless an
unexpected error occurs.

See ``api_v2/docs/batch-responses.md`` for the 200/207/400 status
convention and per-item error code vocabulary.

**Required OAuth scope:** `quizzes:write`

## Request body

- QuizCreateRequestEnvelope — JSON:API envelope for POST /v2/quizzes/. The ``data`` field is always an array — single-create is a list of one. Cap enforcement (<=100 items) lives in the endpoint, NOT here — that gives us HTTP 400 via ``ValidationError`` instead of Pydantic's 422.
  - `data` QuizCreateData[], required — List of quiz items to create.
    - `type` 'quizzes', required — Must be "quizzes".
    - `attributes` QuizCreateRequest, required — Request schema for creating a new quiz. Only ``name`` is required. Every other field defaults to the same value as the underlying ``Quiz`` model column, so ``model_dump()`` produces a dict that can be splatted straight into ``Quiz.objects.create()`` without overriding any model default with an unintended value. Numeric and choice constraints are ported from ``quiz.models.Quiz`` and ``api/v1/quizzes`` serializers: ``passing_percentage_correct`` is capped at 100, ``max_attempts`` / ``limit_question_count`` are non-negative and capped at the int32 column limit, and ``time_limit_seconds`` is bounded by ``QUIZ_MAX_TIME_LIMIT_SECONDS``. ``extra="forbid"`` rejects unknown attribute keys with a 422 at the schema layer (matching ``QuizUpdateRequest`` and the ``api_v2.groups.schemas`` precedent) rather than silently dropping them.
      - `name` string, required — Quiz name.
      - `description_html` string — Optional HTML description shown on the quiz start screen.
      - `passing_percentage_correct` integer — Percentage of questions a student must answer correctly to pass (0-100).
      - `max_attempts` integer — Maximum attempts allowed. 0 means unlimited.
      - `require_correct_response` boolean — If true, students must answer each question correctly before proceeding.
      - `randomize_questions` boolean — If true, questions are presented in a random order.
      - `limit_question_count` integer — Number of questions presented (selected at random). 0 means all questions.
      - `randomize_answers` boolean — If true, answer choices are presented in a random order.
      - `show_results_on_failure` boolean — If true, students who fail can review their submitted answers and per-question status.
      - `show_question_feedback` boolean — If true, students receive per-question feedback on auto-graded questions.
      - `time_limit_seconds` integer, nullable — Time limit in seconds, or null for unlimited time.
      - `skip_start_screen` boolean, nullable — If true, the start screen and description are hidden. May be null.
      - `alignment` 'left' | 'center' | 'right' — Text alignment for the description and start screen (left, center, or right).

## Response `200`

OK

- BatchResultEnvelopeQuizResource
  - `data` union[], required — Per-item results in request order.
    - union
      - BatchSucceededItemQuizResource
        - `status` 'succeeded'
        - `id` string, nullable — Resource ID of the created or updated resource.
        - `result` QuizResource, required — JSON:API resource object for a quiz.
          - `type` 'quizzes' — Always "quizzes".
          - `id` string, required — Opaque quiz ID. Use in URL paths.
          - `attributes` QuizAttributes, required — Attributes of a quiz resource object. Fields mirror the ``quiz.models.Quiz`` columns exposed on the public API. ``organization`` and internal primary keys are deliberately not exposed — ``id`` (obfuscated) and ``external_id`` (UUID5) are the only identifiers a client sees.
            - `name` string, required — Quiz name.
            - `description_html` string — HTML description shown on the quiz start screen.
            - `passing_percentage_correct` integer, required — Percentage of questions a student must answer correctly to pass.
            - `max_attempts` integer, required — Maximum attempts allowed. 0 means unlimited.
            - `require_correct_response` boolean, required — If true, students must answer each question correctly before proceeding.
            - `randomize_questions` boolean, required — If true, questions are presented in a random order.
            - `limit_question_count` integer, required — Number of questions presented (selected at random). 0 means all questions.
            - `randomize_answers` boolean, required — If true, answer choices are presented in a random order.
            - `show_results_on_failure` boolean, required — If true, students who fail can review their submitted answers and per-question status.
            - `show_question_feedback` boolean, required — If true, students receive per-question feedback on auto-graded questions.
            - `time_limit_seconds` integer, nullable — Time limit in seconds, or null for unlimited time.
            - `skip_start_screen` boolean, nullable — If true, the start screen and description are hidden. May be null.
            - `alignment` 'left' | 'center' | 'right', required — Text alignment for the description and start screen.
            - `external_id` string, required — Stable UUID5 for cross-system correlation.
            - `created_at` string, date-time, required — Timestamp when the quiz was created.
            - `modified_at` string, date-time, required — Timestamp when the quiz was last modified.
          - `links` object, nullable — Self and related links.
      - BatchFailedItem — Wrapper for a failed item in a 207 batch response.
        - `status` 'failed'
        - `id` string, nullable — Echoed resource ID if the input identified a row.
        - `error` BatchItemError, required — Per-item error inside a 207 batch response. Aligned with JSON:API ``ErrorObject`` field naming (``code`` rather than ``reason``) so consumers can reuse error-handling logic across document-level errors (``ErrorObject`` in ``ErrorEnvelope``) and per-item errors (here). Differences from ``ErrorObject``: ``status``/``title`` are omitted because they're redundant for the 207-batch context (HTTP status is on the envelope, and the title is derivable from ``code``).
          - `code` 'duplicate_email' | 'duplicate_in_batch' | 'duplicate_name' | 'validation_error' | 'not_found' | 'internal_error' | 'not_in_domain' | 'already_enrolled' | 'already_published', required — Machine-readable error code.
          - `source` object, nullable — Pointer to the offending input slot, e.g. {"pointer": "/data/0/attributes/email"}.
          - `detail` string, nullable — Human-readable explanation of the error.
  - `summary` BatchSummary, required — Aggregate counts for a 207 batch response. Invariant: ``succeeded + failed == total``. Enforced by ``@model_validator``.
    - `total` integer, required — Total number of items submitted.
    - `succeeded` integer, required — Number of items that succeeded.
    - `failed` integer, required — Number of items that failed.

## Other responses

- `207` — Multi-Status
- `400` — Bad Request
- `401` — Unauthorized
- `403` — Forbidden
- `422` — Unprocessable Entity
- `500` — Internal Server Error

---

[API](https://skmtc.net/skilljar/apis/skilljar-api-v2.md) · [All operations](https://skmtc.net/skilljar/apis/skilljar-api-v2/llms.txt) · [OpenAPI document](https://skmtc-service-staging.skmtc.workers.dev/v1/apis/skilljar/skilljar-api-v2/revisions/f8025de20169/schema)
