> ## Documentation Index
> Fetch the complete documentation index at: https://komga-sdk.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Collections

> Organize series into curated collections

Collections group related **series**, such as a summer reading theme, an award shortlist, or a favorite set. A collection stores series IDs; use [Read Lists](/guides/read-lists) when the items are individual books.

Generated operations return `{ data, error, request, response }`. Pass flat parameters first and `{ client }` second. Collection creation and updates require the **ADMIN** role; listing and reading collections follows the user's library permissions.

## List collections

```typescript theme={null}
import { getCollections } from 'komga-sdk';

const result = await getCollections(
  {
    page: 0,
    size: 20,
    search: 'award',
    library_id: ['library-1'],
  },
  { client },
);

if (result.data) {
  console.log(`Found ${result.data.totalElements ?? 0} collections`);
  for (const collection of result.data.content ?? []) {
    console.log(`${collection.name} - ${collection.seriesIds.length} series`);
  }
}
```

`search`, `library_id`, `page`, `size`, and `unpaged` are flat query parameters. Use `unpaged: true` when a client needs to inspect every collection rather than walking pages.

## Get a collection by ID

```typescript theme={null}
import { getCollectionById } from 'komga-sdk';

const result = await getCollectionById({ id: 'collection-123' }, { client });

if (result.data) {
  console.log(`${result.data.name}: ${result.data.seriesIds.length} series`);
  console.log(`Ordered: ${result.data.ordered}`);
}
```

## Create a collection

The generated body parameter is `collectionCreationDto`:

```typescript theme={null}
import { createCollection } from 'komga-sdk';

const result = await createCollection(
  {
    collectionCreationDto: {
      name: 'Award Winners 2024',
      ordered: true,
      seriesIds: ['series-1', 'series-2', 'series-3'],
    },
  },
  { client },
);

if (result.data) console.log(`Created collection: ${result.data.name}`);
```

Use `ordered: true` when the sequence of series is meaningful. With `ordered: false`, consumers should treat `seriesIds` as a grouping rather than a reading order.

## Update a collection

`collectionUpdateDto` is partial. Include `seriesIds` when changing membership or order; the supplied array replaces the collection's current series list, so fetch the collection first when adding one ID without dropping existing entries:

```typescript theme={null}
import { updateCollectionById } from 'komga-sdk';

await updateCollectionById(
  {
    id: 'collection-123',
    collectionUpdateDto: {
      name: 'Updated Collection Name',
      ordered: true,
      seriesIds: ['series-1', 'series-2', 'series-4'],
    },
  },
  { client },
);
```

## Delete a collection

Deleting a collection does not delete any of its series:

```typescript theme={null}
import { deleteCollectionById } from 'komga-sdk';

await deleteCollectionById({ id: 'collection-123' }, { client });
```

## Find collections for a series

This endpoint returns an array, not a paginated response:

```typescript theme={null}
import { getCollectionsBySeriesId } from 'komga-sdk';

const result = await getCollectionsBySeriesId({ seriesId: 'series-123' }, { client });
for (const collection of result.data ?? []) {
  console.log(`${collection.name} (${collection.ordered ? 'ordered' : 'unordered'})`);
}
```

## Thumbnails

Komga can expose a selected poster for a collection and can retain several uploaded thumbnails. Thumbnail reads return binary data, uploads return thumbnail metadata, and deletes return an accepted/no-content result.

### Read a collection thumbnail

```typescript theme={null}
import { getCollectionThumbnail } from 'komga-sdk';

const result = await getCollectionThumbnail({ id: 'collection-123' }, { client });
const imageElement = document.querySelector<HTMLImageElement>('#collection-thumbnail');
if (result.data && imageElement) {
  const thumbnailUrl = URL.createObjectURL(result.data);
  imageElement.src = thumbnailUrl;
  imageElement.onload = () => URL.revokeObjectURL(thumbnailUrl);
}
```

### Upload a custom thumbnail

The upload parameter is a flat `file` value. Set `selected: true` when this upload should become the collection's selected poster; omit it when storing an alternate thumbnail.

```typescript theme={null}
import { addUserUploadedCollectionThumbnail } from 'komga-sdk';

const file = new File([imageBlob], 'thumbnail.jpg', { type: 'image/jpeg' });
await addUserUploadedCollectionThumbnail(
  { id: 'collection-123', file, selected: true },
  { client },
);
```

### Delete a custom thumbnail

Use the collection ID and the uploaded thumbnail ID returned by the thumbnail-list operation:

```typescript theme={null}
import { deleteUserUploadedCollectionThumbnail } from 'komga-sdk';

await deleteUserUploadedCollectionThumbnail(
  { id: 'collection-123', thumbnailId: 'thumbnail-456' },
  { client },
);
```

## Workflows

### Build a “Recently Added” collection

`getSeries` uses the same flat pagination/sort parameters and a required `seriesSearch` body. This workflow keeps the newest series in a defined order:

```typescript theme={null}
import { createCollection, getSeries } from 'komga-sdk';

async function createRecentSeriesCollection() {
  const seriesResult = await getSeries(
    {
      page: 0,
      size: 10,
      sort: ['created,desc'],
      seriesSearch: {},
    },
    { client },
  );

  const seriesIds = (seriesResult.data?.content ?? []).map((series) => series.id);
  if (seriesIds.length === 0) return;

  return createCollection(
    {
      collectionCreationDto: {
        name: 'Recently Added',
        ordered: true,
        seriesIds,
      },
    },
    { client },
  );
}
```

### Organize series by genre

`seriesSearch.fullTextSearch` is the generated search field. Create a collection only when a genre has matches:

```typescript theme={null}
import { createCollection, getSeries } from 'komga-sdk';

async function organizeByGenre(genres: string[]) {
  for (const genre of genres) {
    const seriesResult = await getSeries(
      {
        page: 0,
        size: 100,
        sort: ['metadata.title,asc'],
        seriesSearch: { fullTextSearch: genre },
      },
      { client },
    );

    const seriesIds = (seriesResult.data?.content ?? []).map((series) => series.id);
    if (seriesIds.length === 0) continue;

    await createCollection(
      {
        collectionCreationDto: {
          name: `${genre} Series`,
          ordered: false,
          seriesIds,
        },
      },
      { client },
    );
  }
}
```

### Add a series without losing existing order

Read the current list, append only if the series is not already present, and send the complete replacement list:

```typescript theme={null}
import { getCollectionById, updateCollectionById } from 'komga-sdk';

async function addSeriesToCollection(collectionId: string, seriesId: string) {
  const current = await getCollectionById({ id: collectionId }, { client });
  if (!current.data || current.data.seriesIds.includes(seriesId)) return current;

  return updateCollectionById(
    {
      id: collectionId,
      collectionUpdateDto: {
        seriesIds: [...current.data.seriesIds, seriesId],
      },
    },
    { client },
  );
}
```

## Collections vs Read Lists

| Feature | Collections | Read Lists |
| - | - | - |
| Contains | Series | Individual books |
| Ordering | Optional `ordered` flag | Optional `ordered` flag |
| Typical use | Group related series | Define a book-by-book sequence |
| Navigation | Browse series | Next/previous book helpers |
| Deletion | Removes the grouping only | Removes the list only; books remain |

## Next steps

* Organize individual books with [Read Lists](/guides/read-lists).
* Manage progress with [Read Progress](/guides/read-progress).
* Manage series with the [Series guide](/guides/series).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.