---
title: "Unbind question banks from a quiz"
method: DELETE
path: "/v2/quizzes/{quiz_id}/question-banks/"
tags: ["Quizzes"]
---

# Unbind question banks from a quiz

`DELETE /v2/quizzes/{quiz_id}/question-banks/`

## Request

- Base URL: `https://api.skilljar.com` (the document declares no server; this is the origin it was published from)
- URL: `https://api.skilljar.com/v2/quizzes/{quiz_id}/question-banks/`
- Auth: HTTP bearer
- TypeScript: <https://skmtc.dev/skilljar/apis/skilljar-api-v2/docs/v2/quizzes/:quiz_id/question-banks/delete.ts> (a function with typed inputs; its response is checked against the schema)

Unbind (HARD-delete) up to BATCH_MAX_ITEMS assignments from a quiz.

The ``quiz_id`` path resource is resolved once up front (document-level 404).
Within-batch duplicate detection is first-wins on ``question_bank_id``
(mirrors ``delete_groups``); subsequent duplicates are pre-marked
``duplicate_in_batch`` without reaching the service. Each surviving item is
processed in its own atomic block — a bank not assigned to this quiz is a
per-item ``not_found``. ``QuestionBankAssignment`` is not a SoftDeletionModel,
so the rows are permanently removed. Response is 200 if all succeed, 207 if
any fail, 400 if the envelope is empty or over the cap.

See ``api_v2/docs/batch-responses.md`` for the envelope shape.

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

## Path parameters

- `quiz_id` string, required

## Request body

- QuestionBankAssignmentDeleteRequestEnvelope: JSON:API envelope for DELETE /v2/quizzes/{quiz_id}/question-banks/ batch unbind. 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` QuestionBankAssignmentDeleteData[], required: List of unbind items.
    - `type` 'question-bank-assignments', required: Must be "question-bank-assignments".
    - `attributes` QuestionBankAssignmentDeleteAttributes, required: Attributes of an unbind item — the ``question_bank_id`` natural key only.
      - `question_bank_id` string, required: Obfuscated ID of the question bank to unbind.

## Response `200`

OK

- BatchDeleteResultEnvelope: 207 Multi-Status response envelope for batch-delete operations. Distinct from ``BatchResultEnvelope`` so the generic ``T`` on the latter stays bound to a real resource type and consumers reading ``BatchSucceededItem.result`` keep their non-null contract. Items appear in the same order as the request inputs. ``summary.succeeded`` counts ``BatchDeletedItem`` rows — the field name reflects "operation did not fail" rather than the verb-specific outcome (which is on every item's own ``status`` discriminator).
  - `data` union[], required: Per-item results in request order.
    - union
      - BatchDeletedItem: Wrapper for a successfully deleted item in a 207 batch-delete response. The ``status`` discriminator is ``"deleted"`` (not ``"succeeded"``) so the outcome is explicit on every row without inspecting the rest of the object. There is no ``result`` field: the row has been removed and has no current resource representation to echo. ``id`` semantics depend on the endpoint: - **Resource-keyed deletes** (e.g. ``DELETE /v2/groups/``): the deleted resource's own obfuscated id, echoed from the request. - **Relationship sub-resource deletes** (e.g. ``DELETE /v2/groups/{id}/relationships/published-course-visibility/``): the related-resource id (here, ``published_course_id``), echoed from the request. The deleted ``PublishedCourseGroupVisibility`` row's own obfuscated id is not surfaced because clients identify these rows by the relationship tuple, not by their own id. See ``api_v2/docs/groups.md`` §"Visibility overrides (relationship sub-resource)" for the rationale on the relationship variant.
        - `status` 'deleted'
        - `id` string, required: Obfuscated id echoed from the request. For resource-keyed deletes this is the deleted resource's own id; for relationship sub-resource deletes this echoes the related-resource id (e.g. ``published_course_id``).
      - 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' | 'already_in_path', 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
- `404`: Not Found
- `406`: Not Acceptable: Accept rules out application/vnd.api+json.
- `413`: Payload Too Large: the request body exceeds the maximum allowed size.
- `415`: Unsupported Media Type: Content-Type must be application/json or application/vnd.api+json.
- `422`: Unprocessable Entity
- `500`: Internal Server Error

## Changes

- **2026-10-16** `bed169429eb2`: 2 warning, 3 info
  - added the new `already_in_path` enum value to the `data/items/anyOf[subschema #2: BatchFailedItem]/error/code` response property for the response status `200`
  - added the new `already_in_path` enum value to the `data/items/anyOf[subschema #2: BatchFailedItem]/error/code` response property for the response status `207`
  - added the non-success response with the status `406`
  - added the non-success response with the status `413`
  - …1 more
- **2026-09-15** `9fc34e55336d`: 8 info
  - media type `application/json` was changed to a more specific media type `application/vnd.api+json` for the response status `200`
  - media type `application/json` was changed to a more specific media type `application/vnd.api+json` for the response status `207`
  - media type `application/json` was changed to a more specific media type `application/vnd.api+json` for the response status `400`
  - media type `application/json` was changed to a more specific media type `application/vnd.api+json` for the response status `401`
  - …4 more

[Operation history](https://skmtc.dev/skilljar/apis/skilljar-api-v2/changes/v2/quizzes/:quiz_id/question-banks/delete.md)

---

[API](https://skmtc.dev/skilljar/apis/skilljar-api-v2.md) - [All operations](https://skmtc.dev/skilljar/apis/skilljar-api-v2/llms.txt) - [OpenAPI document](https://skmtc.dev/skilljar/apis/skilljar-api-v2/revisions/fbda30ce1742?raw)
