> ## Documentation Index
> Fetch the complete documentation index at: https://help.skillsdb.com/llms.txt
> Use this file to discover all available pages before exploring further.

# The Learning Item object

> A catalogue entry in the training library — the course, video, document or session that gets assigned to people as a learning plan

<Info>
  **Quick Summary:** A learning item is an entry in your company's training catalogue. It carries a title, an optional link or file, a learning type and up to four further references (provider, format, audience, course level) drawn from the company's [reference lists](/api-docs/resources/learning-reference-list). Items are attached to skills and careers, labelled, and assigned to people as [learning plans](/api-docs/resources/learning-plan). Deleting is guarded.
</Info>

## Attributes

| Field                                                  | Type                   | Description                                                                        |
| ------------------------------------------------------ | ---------------------- | ---------------------------------------------------------------------------------- |
| `id`                                                   | string                 | Opaque learning item id                                                            |
| `title`                                                | string                 |                                                                                    |
| `description`                                          | string \| null         |                                                                                    |
| `url`                                                  | string \| null         | Link to the content. Unique within the company                                     |
| `durationHours`                                        | number \| null         |                                                                                    |
| `cost`                                                 | number \| null         |                                                                                    |
| `costDescription`                                      | string \| null         | Free text next to the cost                                                         |
| `type`                                                 | `{ id, name }` \| null | Learning type (reference list)                                                     |
| `provider`                                             | `{ id, name }` \| null | Training provider (reference list)                                                 |
| `format`                                               | `{ id, name }` \| null | Training format (reference list)                                                   |
| `audience`                                             | `{ id, name }` \| null | Training audience (reference list)                                                 |
| `courseLevel`                                          | `{ id, name }` \| null | Training course level (reference list)                                             |
| `imageUrl`                                             | string \| null         | Display image                                                                      |
| `hasFile`                                              | boolean                | An attached file exists; fetch it with the download-url operation                  |
| `referenceId`                                          | string \| null         | Caller-supplied external identifier                                                |
| `labels`                                               | `[{ id, name }]`       | Labels attached to the item                                                        |
| `skillNames`                                           | string\[]              | Names of the skills the item is attached to                                        |
| `careerNames`                                          | string\[]              | Names of the careers the item appears on                                           |
| `assignedCount` / `inProgressCount` / `completedCount` | integer                | Learning plans for active people, by state                                         |
| `averageRating`                                        | number \| null         | Mean of the thumbs ratings (0 down, 1 up, 2 double up); `null` until someone rates |
| `createdAt` / `updatedAt`                              | string \| null         |                                                                                    |

The five references are published as `{ id, name }` pairs resolved against the reference lists. The legacy free-text provider, level, format and audience columns the app still stores are **not** part of the object.

```json theme={null}
{
  "id": "512",
  "title": "Forklift Safety Refresher",
  "description": "Annual refresher for licensed operators.",
  "url": "https://learn.example.com/forklift",
  "durationHours": 2.5,
  "cost": 120,
  "costDescription": null,
  "type": { "id": "3", "name": "Online course" },
  "provider": { "id": "1", "name": "Acme Learning" },
  "format": null,
  "audience": null,
  "courseLevel": null,
  "imageUrl": null,
  "hasFile": false,
  "referenceId": "LMS-4471",
  "labels": [{ "id": "12", "name": "Safety" }],
  "skillNames": ["Forklift Operation"],
  "careerNames": [],
  "assignedCount": 40,
  "inProgressCount": 12,
  "completedCount": 25,
  "averageRating": 1.6,
  "createdAt": "2026-01-12T08:30:00.000Z",
  "updatedAt": null
}
```

## Operations

### Catalogue

| Operation                          | Method and path                         | Requires scope   |
| ---------------------------------- | --------------------------------------- | ---------------- |
| List learning items                | `GET /api/v1/learning-items`            | `learning:read`  |
| Get a learning item                | `GET /api/v1/learning-items/{id}`       | `learning:read`  |
| Create a learning item             | `POST /api/v1/learning-items`           | `learning:write` |
| Create a learning item with a file | `POST /api/v1/learning-items/with-file` | `learning:write` |
| Update a learning item             | `PATCH /api/v1/learning-items/{id}`     | `learning:write` |
| Delete a learning item             | `DELETE /api/v1/learning-items/{id}`    | `learning:write` |

The list is filterable by `title`, `id`, `labels`, `people`, `rating`, `cost`, `duration` and `skill` — the app's library filters under the same names — and sortable by `title`, `cost`, `duration`, `rating` and `id`. Pending and rejected [suggestions](/api-docs/resources/learning-suggestion) are not part of the catalogue.

**Create** needs `title` and `typeId`; `providerId`, `formatId`, `audienceId`, `courseLevelId`, `labelIds` and `skillIds` are optional. A reference id that is not an entry of the company's list is a field-level [validation error](/api-docs/errors/validation-error).

**Create with a file** is the two-step path: send the same body plus `fileName`, receive `{ item, uploadUrl }`, then `PUT` the file's bytes to `uploadUrl` (a short-lived pre-signed URL). `hasFile` is true from the moment the item exists.

**Update** is partial: a field that is absent is left alone, an explicit `null` clears it. A `url` already used by another item is a [409 conflict](/api-docs/errors/conflict).

**Delete** is guarded. If the item is assigned to anyone or attached to a skill or career the call answers [409 conflict](/api-docs/errors/conflict):

```json theme={null}
{
  "type": "https://help.skillsdb.com/api-docs/errors/conflict",
  "title": "Conflict",
  "status": 409,
  "detail": "The learning item is assigned to people or attached to skills or careers. Retry with ?confirm=true to delete it anyway: every assignment and attachment is removed with it.",
  "confirmRequired": true,
  "dependencies": { "learningPlans": 40, "skills": 2, "careers": 1 }
}
```

Retry with `?confirm=true` to delete the item together with every assignment and attachment.

### Skills

| Operation                        | Method and path                                       | Requires scope   |
| -------------------------------- | ----------------------------------------------------- | ---------------- |
| List an item's skills            | `GET /api/v1/learning-items/{id}/skills`              | `learning:read`  |
| Attach a skill                   | `POST /api/v1/learning-items/{id}/skills`             | `learning:write` |
| Feature or unfeature for a skill | `PATCH /api/v1/learning-items/{id}/skills/{skillId}`  | `learning:write` |
| Detach a skill                   | `DELETE /api/v1/learning-items/{id}/skills/{skillId}` | `learning:write` |

Attach takes `{ "skillId": "77", "featured": true }` (`featured` optional) and is idempotent — attaching a skill the item already carries succeeds without change. Detaching a skill the item does not carry is a no-op. Every call answers the item's current skills, each as `{ skill: { id, name }, featured }`. A featured item is shown first for the skill.

### Careers, labels, ratings and file

| Operation                           | Method and path                                | Requires scope   |
| ----------------------------------- | ---------------------------------------------- | ---------------- |
| List the careers an item appears on | `GET /api/v1/learning-items/{id}/careers`      | `learning:read`  |
| Replace an item's labels            | `PUT /api/v1/learning-items/{id}/labels`       | `learning:write` |
| Get rating counts                   | `GET /api/v1/learning-items/{id}/ratings`      | `learning:read`  |
| Get a download URL for the file     | `GET /api/v1/learning-items/{id}/download-url` | `learning:read`  |

**Replace labels** takes `{ "labelIds": ["12", "15"] }` and sets the complete set; an empty list clears every label. Incremental attach and detach live on the [Label](/api-docs/resources/label) resource. **Ratings** answers `{ thumbsDown, thumbsUp, doubleThumbsUp }`. **Download URL** answers a short-lived pre-signed URL; an item without a file is a [404](/api-docs/errors/not-found).

## Permissions

Any person who can see the training library in the app can read items here. **Creating and updating items, and attaching skills or labels, requires a person who may author training in the app** (global administrators, managers, full-access users and employees); administrators, directors and executives receive [insufficient-permissions](/api-docs/errors/insufficient-permissions) even when the key holds `learning:write`. **Deleting is restricted to global administrators.**

## Excluded

Assigning an item to a person, progress and completion, and thumbs ratings belong to the [Learning Plan](/api-docs/resources/learning-plan) resource. Proposals from people who cannot author the catalogue are [Learning Suggestions](/api-docs/resources/learning-suggestion). CSV import and the "fetch the page's title" convenience when pasting a URL are in-app paths.

## Related errors

* [not-found](/api-docs/errors/not-found) — the item, or a skill named in a feature or detach call, is not in your company or not attached
* [validation-error](/api-docs/errors/validation-error) — a missing title or type, a reference id that is not in the company's list, an unknown skill or label
* [conflict](/api-docs/errors/conflict) — a duplicate URL; the guarded delete
* [insufficient-permissions](/api-docs/errors/insufficient-permissions) — the acting person may not author or delete training
