---
title: "Move assets to another library"
method: POST
path: "/api/assets/move"
tags: ["assets"]
---

# Move assets to another library

`POST /api/assets/move`

## Request

- Base URL: `https://api.gumnut.ai`
- URL: `https://api.gumnut.ai/api/assets/move`
- Auth: one of:
  - HTTP bearer
  - HTTP bearer

Moves the given assets from one library to another, keeping their IDs and stored files. The caller must own the source library and either own the destination library or be a collaborator on it. A scoped credential must cover both libraries and allow both `delete_permanently` and `write`. Moved assets leave the source library's albums, people, and stacks.

Returns 200 with one result per asset even when some assets could not be moved. Each result describes the asset's state when the request ran, so repeating a request is safe: an asset already in the destination library is reported as a success.

Returns 409 when a concurrent change interrupted the request; nothing was moved, so retry it unchanged. Returns 503 while moving assets is turned off; retry later.

## Request body

- MoveAssetsRequest: Request body for moving assets from one library to another.
  - `source_library_id` string, required: Library the assets are in now. The caller must own it.
  - `destination_library_id` string, required: Library to move the assets into. The caller must own it or be a collaborator on it. Must differ from `source_library_id`.
  - `asset_ids` string[], required: Asset IDs (each with the `asset_` prefix) to move.

## Response `200`

Successful Response

- MoveAssetsResponse
  - `data` AssetMoveResult[], required: One result per distinct requested asset ID, in request order.
    - `asset_id` string, required: Requested asset ID.
    - `outcome` 'moved' | 'already_in_destination' | 'failed', required: What a move request did with one asset. - `moved`: the asset is now in the destination library. - `already_in_destination`: the asset was already in the destination library, so nothing changed. This is a success. - `failed`: the asset was not moved; `failure` says why.
    - `failure` 'unavailable' | 'changed_source' | 'duplicate' | 'quota' | 'busy': Why an asset was not moved. - `unavailable`: the asset does not exist, is in the trash, or is not accessible. - `changed_source`: the asset is no longer in the source library. - `duplicate`: the destination library already holds an asset with the same original file, possibly in its trash. - `quota`: the destination library has no storage room for the asset. - `busy`: another operation is changing the asset. Retry the request.

## Other responses

- `401`: Missing, invalid, or expired credentials.
- `403`: The credentials are valid but not authorized for this operation — for example an API key whose action or library scope excludes it, or a credential type this operation does not accept.
- `404`: Not found
- `409`: A concurrent change interrupted the request and nothing was moved. Retry the request unchanged. Clerk sign-in could not be associated with the existing account. Resolve the identity conflict before retrying sign-in; repeated requests with unchanged verification and identity state will not resolve it.
- `422`: Validation Error
- `429`: Rate limit exceeded, or too many of your requests are already in progress. Retry after the interval in the `Retry-After` header.
- `503`: Moving assets is turned off; retry later. Authentication provider outages also return 503; retry sign-in later.

## Changes

- **2026-10-16** `7c2860d9eb88`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/gumnut-ai/apis/gumnut-api/changes/api/assets/move/post.md)

---

[API](https://skmtc.dev/gumnut-ai/apis/gumnut-api.md) - [All operations](https://skmtc.dev/gumnut-ai/apis/gumnut-api/llms.txt) - [OpenAPI document](https://skmtc.dev/gumnut-ai/apis/gumnut-api/revisions/4dcbf19e1db8?raw)
