---
title: "Update web packages"
method: PATCH
path: "/v2/web-packages/"
tags: ["Web Packages"]
---

# Update web packages

`PATCH /v2/web-packages/`

Update up to ``BATCH_MAX_ITEMS`` web packages in a single request.

``title`` is the only writable attribute. ``type``, ``state`` and
``base_path`` are server-owned, and ``display_name`` is derived from the
title, state and archive filename — a client that sends any of them has them
silently ignored rather than rejected.

Note ``display_name`` only reflects ``title`` once the package reaches
``READY``. While a package is ``PROCESSING`` or ``ERROR`` its display name is
the state and filename, so a title change appears to have no effect on it
until ingestion finishes. That is ``WebPackage.build_display_name``'s
behaviour, unchanged by this API.

Returns 200 if all items succeed, 207 if any fail, 400 if the envelope is
empty or over the cap.

**Required OAuth scope:** `web-packages:write`

## Request body

- WebPackageUpdateRequestEnvelope — JSON:API envelope for PATCH /v2/web-packages/. The ``data`` field is always an array — single-update is a list of one. Cap enforcement lives in the endpoint, matching the create-endpoint convention.
  - `data` WebPackageUpdateData[], required — List of web package items to update.
    - `type` 'web-packages', required — Must be "web-packages".
    - `id` string, required — Obfuscated ID of the web package to update.
    - `attributes` WebPackageUpdateRequest, required — Request attributes for PATCH /v2/web-packages/. ``title`` is the only writable field. ``type``, ``state`` and ``base_path`` are server-owned, and ``display_name`` is derived — ``WebPackage.save()`` recomputes it unconditionally, so accepting it here would publish a contract that silently discards caller input.
      - `title` string, required — New title for the package.

## Response `200`

OK

- BatchResultEnvelopeWebPackageResource
  - `data` union[], required — Per-item results in request order.
    - union
      - BatchSucceededItemWebPackageResource
        - `status` 'succeeded'
        - `id` string, nullable — Resource ID of the created or updated resource.
        - `result` WebPackageResource, required — JSON:API resource object for a web package.
          - `type` 'web-packages' — Always "web-packages".
          - `id` string, required — Opaque web package ID. Use in URL paths.
          - `attributes` WebPackageAttributes, required — Attributes of a web package resource object. ``type`` and ``state`` are deliberately typed as loose ``str`` rather than ``Literal``, matching ``LessonAttributes.type``: a new choice added to ``WebPackage.PACKAGE_TYPE_CHOICES`` must never break serialization of existing rows. The valid values are documented in the field descriptions. Note ``type`` lives inside ``attributes`` even though JSON:API reserves that name at the resource level. This follows the established v2 precedent (``LessonAttributes.type``) and is what GE-249998 AC2 asks for.
            - `type` string, required — Package format: SCORM, SCORM_2004, XAPI, AICC, or HTML.
            - `state` string, required — Ingestion state: PROCESSING while the package is being fetched and unpacked, READY once usable, ERROR if the archive could not be processed. Poll this field after create.
            - `title` string, required — Best-guess title parsed from the package manifest.
            - `display_name` string, required — Server-derived label shown in the dashboard. Recomputed on every save from the title, state and archive filename — it is not directly settable.
            - `base_path` string, required — Base location in storage of the unpacked package content.
            - `created_at` string, date-time, required — Timestamp when the web package was created.
            - `modified_at` string, date-time, required — Timestamp when the web package 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
- `404` — Not Found
- `422` — Unprocessable Entity

---

[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)
