---
title: "Update question banks"
method: PATCH
path: "/v2/question-banks/"
tags: ["Question Banks"]
---

# Update question banks

`PATCH /v2/question-banks/`

Update up to BATCH_MAX_ITEMS question banks 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 question-bank
``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:** `question-banks:write`

## Request body

- QuestionBankBatchUpdateRequestEnvelope — JSON:API envelope for PATCH /v2/question-banks/ 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` QuestionBankBatchUpdateData[], required — List of question-bank update items.
    - `type` 'question-banks', required — Must be "question-banks".
    - `id` string, required — Obfuscated ID of the question bank to update.
    - `attributes` QuestionBankUpdateRequest, required — Request schema for partial updates of a question bank. ``name`` is the only writable field. PATCH semantics are preserved by a Python-level sentinel default 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)``. The sentinel default is stripped from the emitted JSON Schema so SDK generators do not advertise it. ``extra="forbid"`` rejects unknown attribute keys with a 422 at the schema layer (mirrors ``QuizUpdateRequest``). The service also guards with ``_ALLOWED_QB_UPDATE_FIELDS`` as defense-in-depth for any non-HTTP caller.
      - `name` string — New question bank name. Must be non-empty when provided.

## Response `200`

OK

- BatchResultEnvelopeQuestionBankResource
  - `data` union[], required — Per-item results in request order.
    - union
      - BatchSucceededItemQuestionBankResource
        - `status` 'succeeded'
        - `id` string, nullable — Resource ID of the created or updated resource.
        - `result` QuestionBankResource, required — JSON:API resource object for a question bank.
          - `type` 'question-banks' — Always "question-banks".
          - `id` string, required — Opaque question bank ID. Use in URL paths.
          - `attributes` QuestionBankAttributes, required — Attributes of a question-bank resource object. Fields mirror the ``quiz.models.QuestionBank`` 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 — Question bank name.
            - `external_id` string, required — Stable UUID5 for cross-system correlation.
            - `created_at` string, date-time, required — Timestamp when the question bank was created.
            - `modified_at` string, date-time, required — Timestamp when the question bank 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)
