> For the complete documentation index, see [llms.txt](https://docs.unitlab.ai/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.unitlab.ai/api-sdk-cli/clis/cli-assets-and-folders.md).

# CLI: Assets and folders

Upload files and folders into Unitlab Assets with the CLI. Manage destination paths, tags, metadata, cloud folders and JSON rules for Data Groups.

Upload reusable source media into Assets, organize it in folders and create Data Groups before attaching the data to projects. Commands below match Unitlab 3.1.3 and use the [configured workspace API key](https://docs.unitlab.ai/api-sdk-cli/get-started/authentication-and-configuration).

## Before you start

Run the commands from a directory containing the following real images. Save `grouping.json` and, if used, `metadata.json` alongside the media folder.

```
working-directory/
├── road-views/
│   ├── drive001_front.jpg
│   ├── drive001_left.jpg
│   ├── drive002_front.jpg
│   └── drive002_left.jpg
├── grouping.json  # Rules used in the grouping step
└── metadata.json  # Optional upload metadata
```

Choose one upload option and one grouping option. The examples are alternatives, so do not run every upload command against the same data. Replace `FOLDER_ID` with the upload result's source folder ID, `ASSET_ID` with a returned Asset ID, and `PROJECT_ID` with the destination project's ID. Save the grouping result's `grouped_folder_id` as `GROUPED_FOLDER_ID` for attachment.

## Upload media

{% tabs %}
{% tab title="Named folder" %}

```bash
unitlab assets upload ./road-views --folder "Road views" --tag incoming --json
```

{% endtab %}

{% tab title="Existing folder" %}

```bash
unitlab assets upload ./road-views --folder-id FOLDER_ID --json
```

{% endtab %}

{% tab title="Project-backed path" %}

```bash
unitlab assets upload ./road-views --folder-id PROJECT_ROOT_FOLDER_ID \
  --path collection-01/road-views --json
```

{% endtab %}
{% endtabs %}

The source can be a file or folder. Supported media is discovered recursively. Use `--folder` or `--folder-id`, not both. Without either, the default folder is `Untitled upload`.

Ordinary uploads send each file's basename and do not automatically recreate local subdirectories. `--path` is honored only when `--folder-id` identifies the root Assets folder that backs a project; it is ignored for ordinary Assets folders. For ordinary folders, create real subfolders with `unitlab folders create NAME --parent FOLDER_ID`, then upload each local subfolder to its destination's `--folder-id`. For a project-backed root, `--path` selects one destination for the whole call. Avoid duplicate basenames when later annotation matching relies on filenames.

The result includes `folder_id`, `folder_name`, `uploaded`, `failed` and an `assets` array. Upload exits nonzero if some files failed, while preserving successful uploads. Check an Asset's processing status with:

```bash
unitlab assets status ASSET_ID --json
```

Large media may still be processing after transfer. The CLI has no Assets wait command; use repeated status checks or the [Python Asset waiter](https://docs.unitlab.ai/api-sdk-cli/sdks/assets-and-custom-metadata#wait-for-assets-to-be-ready).

For the four-image example, expect `uploaded: 4` and no entries in `failed`. Verify all four filenames with:

```bash
unitlab folders items FOLDER_ID --json
```

Check processing for each returned Asset before grouping. The `folder_id` identifies the Unitlab Assets destination, not the local `road-views` path.

## Add tags and custom metadata

```bash
unitlab assets upload ./road-views --folder "Night collection" \
  --tag incoming --tag night \
  --custom-metadata '{"source":"camera-7","shift":"night"}' --json
```

Tags and metadata apply to every Asset uploaded by that call. For metadata kept in a file:

```bash
unitlab assets upload ./road-views --folder-id FOLDER_ID \
  --custom-metadata-file metadata.json --json
```

Metadata must be a JSON object or `null`. Inline and file-based metadata options are mutually exclusive.

For example, `metadata.json` can contain:

```json
{
  "collection": "road-views",
  "shift": "night"
}
```

This file supplies metadata for the upload; it is not a list of files to upload.

Replace an existing Asset's metadata:

```bash
unitlab assets set-metadata ASSET_ID \
  --custom-metadata '{"source":"camera-7","shift":"night","approved":true}' --json
```

An update replaces the complete object, so include keys you want to retain. Clear it with:

```bash
unitlab assets set-metadata ASSET_ID --custom-metadata null --json
```

The `approved` field in the example is custom metadata. It does not approve labels or change an item's review state.

## Create and inspect folders

```bash
unitlab folders create "Collection 01" --json
unitlab folders create "Camera 7" --parent FOLDER_ID --json
unitlab folders list --json
unitlab folders list --parent FOLDER_ID --json
unitlab folders detail FOLDER_ID --json
unitlab folders items FOLDER_ID --json
```

`folders list` lists all folders; `--parent` selects a parent's children. Folder creation uses `--parent`, not `--parent-id`.

For a saved cloud connection:

```bash
unitlab folders create "Incoming" --cloud-storage CLOUD_STORAGE_ID \
  --prefix incoming/ --json
unitlab folders sync-cloud FOLDER_ID --json
```

See [CLI cloud storage](https://docs.unitlab.ai/api-sdk-cli/clis/cli-ontologies-and-cloud-storage) for connection discovery.

## Group related files

Upload the source files first. A JSON grouping configuration describes filename matching and layout for existing Assets; it is not a list of file URLs or explicit group members.

{% tabs %}
{% tab title="JSON rules" %}
Save the [Data Group configuration example](https://docs.unitlab.ai/api-sdk-cli/sdks/data-group-automation#define-the-grouping-rules) as `grouping.json` in the working directory. It matches the four filenames shown above and requires both views. First estimate:

```bash
unitlab assets group FOLDER_ID --config grouping.json --preview --json
```

Expect four source files, two valid groups, and no conflicts or skipped groups. Inspect `valid` and resolve errors before creating:

```bash
unitlab assets group FOLDER_ID --config grouping.json --json
```

{% endtab %}

{% tab title="Filename template" %}
For `drive001_front.jpg`, `drive001_left.jpg` and similarly named recordings:

```bash
unitlab assets group FOLDER_ID \
  --template '{session_id}_{view}' \
  --tile-values 'view=front,left' \
  --preview --json
```

Omit `--preview` to create after reviewing the estimate. The template helper allows incomplete groups by default. Use a JSON configuration when both views must be required.
{% endtab %}

{% tab title="Suggested rules" %}

```bash
unitlab assets group FOLDER_ID --preview --json
```

Without a config or template, preview asks the server to suggest a pattern. Creation without a config requires a confident suggestion.
{% endtab %}
{% endtabs %}

Review unmatched files, missing views and conflicts before creation. The result contains `created_count` and `grouped_folder_id`. A new grouped folder is created; originals remain in the source folder. Repeating creation produces another grouped result.

With the complete JSON example, expect `created_count: 2`. These are logical groups and tile assignments in Unitlab, not folders or copied images created on your computer:

```
Grouped result in Unitlab
├── Drive drive001
│   ├── Front camera → drive001_front.jpg
│   └── Left camera  → drive001_left.jpg
└── Drive drive002
    ├── Front camera → drive002_front.jpg
    └── Left camera  → drive002_left.jpg
```

The filename-template option uses the shorter default group names `drive001` and `drive002`; the same files belong together.

This SDK/CLI operation asks you to split folders above 5,000 files or estimates above 1,000 groups. It does not transparently run unlimited grouping jobs.

## Attach the prepared data

Use the returned grouped folder ID when attaching groups:

```bash
unitlab project attach PROJECT_ID --folder GROUPED_FOLDER_ID --preview --json
unitlab project attach PROJECT_ID --folder GROUPED_FOLDER_ID --json
```

Alternatively, create and publish a dataset from that folder with [CLI datasets and versions](https://docs.unitlab.ai/api-sdk-cli/clis/cli-datasets-and-versions). If the selected data includes video, supply the required `--fps`.

After attachment, open the project and inspect a group. Confirm both camera views and the saved layout. Creating the Assets groups alone does not attach them to the project.

## LiDAR and annotation JSON

LiDAR requires the explicit type:

```bash
unitlab assets upload ./recordings --folder "LiDAR" \
  --type pointcloud --dry-run --json
unitlab assets upload ./recordings --folder "LiDAR" \
  --type pointcloud --json
```

The dry run lists scenes without uploading. See [LiDAR preparation](https://docs.unitlab.ai/documentation/annotations/lidar-annotation#prepare-lidar-data) for folders, scene manifests and supported formats.

Annotation JSON is imported through the [Python SDK](/api-sdk-cli/sdks/annotation-imports.md) or [frontend](https://docs.unitlab.ai/documentation/data/import-local-annotations) into existing project media. It is not accepted by `assets upload`, and the CLI has no label-import command in version 3.1.3.

Explore [training-data curation workflows](https://unitlab.ai/en/data-curation).
