---
title: "List assets"
method: GET
path: "/v2/assets/"
tags: ["Assets"]
---

# List assets

`GET /v2/assets/`

List assets for the authenticated organization.

``download_url`` is deliberately absent from this response — generating it signs
a URL per row. Retrieve a single asset to get one.

``query`` is declared for the OpenAPI/MCP contract only: ``CursorPaginator``
reads ``page[size]`` / ``page[cursor]`` straight off ``request.GET``, so the
bound object is intentionally unused here.

**Required OAuth scope:** `assets:read`

## Query parameters

- `page[size]` integer — Maximum number of records to return.
- `page[cursor]` string, nullable — Opaque cursor from the previous page's links.next.

## Response `200`

OK

- PaginatedListResponseAssetResource
  - `data` AssetResource[], required — The resources on this page, in cursor order.
    - `type` 'assets' — Always "assets".
    - `id` string, required — Opaque asset ID. Use in URL paths.
    - `attributes` AssetAttributes, required — Attributes of an asset resource object, as returned on list. ``type`` and ``state`` are deliberately typed as loose ``str`` rather than ``Literal``, matching ``WebPackageAttributes`` and ``LessonAttributes.type``: a new choice added to ``Asset.ASSET_TYPE_CHOICES`` or ``ASSET_STATE_CHOICES`` must never break serialization of existing rows. The valid values are documented in the field descriptions. Note ``type`` here is the **asset kind**, not the JSON:API resource type. Both are spelled ``type`` — the resource-level one is always ``"assets"``, while this one distinguishes a PDF from a video. Living inside ``attributes`` is what keeps them apart on the wire, and follows the ``LessonAttributes.type`` precedent. ``download_url`` is **not** here: signing a URL costs a round trip per row, so it is exposed only on the single-resource response (see ``AssetDetailAttributes``).
      - `type` string, required — Asset kind: PDF, FILE, HTML, VIDEO_BOTR, AUDIO_BOTR, or TEMPLATE. Server-determined from the fetched content — a value supplied on create seeds the row but is provisional and may be corrected during ingest.
      - `state` string, required — Ingestion state: PROCESSING while the asset is being fetched, stored and (for video/audio) handed to JW Platform; READY once that handoff has been accepted; ERROR if it could not be completed. Poll this field after create. READY means accepted for processing, not that transcoding has finished.
      - `title` string, required — Human-readable label. Server-derived from the uploaded filename when not set explicitly.
      - `status_msg` string, required — Reason the asset reached ERROR, or empty when there is none. Generic by design — it never names the host or address a fetch was attempted against.
      - `created_at` string, date-time, required — Timestamp when the asset was created.
      - `modified_at` string, date-time, required — Timestamp when the asset was last modified.
    - `links` object, nullable — Self and related links.
  - `meta` object, nullable — Pagination metadata, e.g. {"page_size": 25}.
  - `links` object, nullable — Pagination links, e.g. {"self": "...", "next": "...", "prev": null}.
  - `has_more` boolean, required — Whether additional pages exist after this one.
  - `next_cursor` string, nullable — Opaque cursor to pass as ``page[cursor]`` for the next page; null on the last page.
  - `included` object[], nullable — Related resources requested via ?include=. Absent when no include was requested.

## Other responses

- `401` — Unauthorized
- `403` — Forbidden
- `404` — Not Found
- `422` — Unprocessable Entity

## Changes

- **2026-09-01** `a3c13d9cb9b7` — 1 info
  - endpoint added

[Change history](https://skmtc.dev/skilljar/apis/skilljar-api-v2/changes/v2/assets/get.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-service-production.skmtc.workers.dev/v1/apis/skilljar/skilljar-api-v2/revisions/91fb0195e524/schema)
