---
title: "Update quizzes"
method: PATCH
path: "/v2/quizzes/"
tags: ["Quizzes"]
---

# Update quizzes

`PATCH /v2/quizzes/`

Update 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 must carry the obfuscated quiz ``id``
and an ``attributes`` object with the fields to update. 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.

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

- QuizBatchUpdateRequestEnvelope — JSON:API envelope for PATCH /v2/quizzes/ batch updates. The ``data`` field is always an array. Cap enforcement (<=100 items) lives in the endpoint so violations produce HTTP 400 rather than Pydantic 422.
  - `data` QuizBatchUpdateData[], required — List of quiz update items.
    - `type` 'quizzes', required — Must be "quizzes".
    - `id` string, required — Obfuscated ID of the quiz to update.
    - `attributes` QuizUpdateRequest, required — Request schema for partial updates of a quiz. Non-nullable fields are typed as non-Optional so Pydantic rejects explicit ``null`` at the type level. ``time_limit_seconds`` and ``skip_start_screen`` are genuinely nullable columns, so they stay ``| None`` and accept an explicit ``null`` to clear/blank them. PATCH semantics are preserved by Python-level sentinel defaults combined with ``model_fields_set``: an omitted field keeps its default but is absent from ``model_fields_set``, so the endpoint forwards only explicitly-provided fields via ``model_dump(exclude_unset=True)``. Sentinel defaults are stripped from the emitted JSON Schema so SDK generators do not advertise them. ``extra="forbid"`` rejects unknown attribute keys with a 422 at the schema layer (mirrors ``api_v2.groups.schemas`` precedent) rather than silently dropping them. The service also guards with ``_ALLOWED_UPDATE_FIELDS`` as defense-in-depth for any non-HTTP caller.
      - `name` string — New quiz name. Must be non-empty when provided.
      - `description_html` string — New HTML description shown on the quiz start screen. Empty string "" is accepted and clears the description.
      - `passing_percentage_correct` integer — New passing percentage (0-100).
      - `max_attempts` integer — New maximum attempts. 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 — New 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)
