> 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-projects-and-data-units.md).

# CLI: projects and Data Units

Upload media files and folders with the Unitlab CLI, monitor Batch Queues, attach datasets and inspect project Data Units with verified commands.

Use the CLI to upload media, manage projects, attach published data and inspect work items. The main example uploads two images, waits for processing, and checks the resulting filenames.

| Task                          | Route                                                                                                                                                               |
| ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Upload media files or folders | CLI commands on this page, Python SDK or frontend.                                                                                                                  |
| Import annotation labels      | [Python SDK annotation imports](/api-sdk-cli/sdks/annotation-imports.md) or [frontend import](https://docs.unitlab.ai/documentation/data/import-local-annotations). |

The current CLI has no annotation-import command. A JSON label file is not imported by `project upload`.

## Before you start

Use Unitlab 3.1.3 and complete [installation](https://docs.unitlab.ai/api-sdk-cli/get-started/install-the-python-sdk-and-cli) and [authentication](https://docs.unitlab.ai/api-sdk-cli/get-started/authentication-and-configuration).

Prepare the following folder and run commands from `road-scenes/`:

```
road-scenes/
└── images/
    ├── road.jpg
    └── crossing.jpg
```

These examples use placeholders, not real IDs:

| Placeholder                                   | Replace it with                                                          |
| --------------------------------------------- | ------------------------------------------------------------------------ |
| `PROJECT_ID`                                  | The `id` of your target project from `project list` or `project create`. |
| `BATCH_QUEUE_ID`                              | The `batch_queue_id` returned by the upload.                             |
| `CLOUD_STORAGE_ID`, `FOLDER_ID`, `DATASET_ID` | The ID of an existing connection, Assets folder, or dataset.             |
| `DATA_UNIT_ID`, `SOURCE_LINK_ID`              | An ID returned by `project data-units` or `project sources`.             |

## Find or create a project

List existing projects and use the intended project ID:

```bash
unitlab project list --json
unitlab project detail PROJECT_ID --json
```

If you need a new project instead, create one:

```bash
unitlab project create "Road scenes" --json
```

Save its returned `id` and use it as `PROJECT_ID` below. To use an existing workspace ontology at creation, add `--ontology ONTOLOGY_ID`, replacing that placeholder with the ontology ID.

## Upload media files and folders

Choose one example. The folder command uploads both example images; the single-file command uploads only `road.jpg`. Running both uploads `road.jpg` again. The video tab is a separate example requiring `drive.mp4`.

{% tabs %}
{% tab title="Folder" %}

```bash
unitlab project upload PROJECT_ID --source ./images --json
```

{% endtab %}

{% tab title="Single file" %}

```bash
unitlab project upload PROJECT_ID --source ./images/road.jpg --json
```

{% endtab %}

{% tab title="Video" %}

```bash
unitlab project upload PROJECT_ID --source ./drive.mp4 --fps 30 --json
```

The example uses 30 FPS. Choose the source frame rate when importing labels for original frames. Without this flag, video upload defaults to 1 FPS.
{% endtab %}
{% endtabs %}

`--source` is required; `--directory` is its alias. Folder collection is recursive, so no recursive flag is needed. Ordinary files are sent with their basenames, not a recreated local folder tree. Use unique filenames when different source folders contain identically named files.

The result includes `total`, `uploaded`, `failed` and `batch_queue_id`. For the valid two-image folder, expect `total: 2`, `uploaded: 2`, and `failed: 0`. Save the queue ID and replace `BATCH_QUEUE_ID` in the next commands with that value. Upload transfer can finish before server processing, and unsupported extensions are skipped. See [supported source files](https://docs.unitlab.ai/documentation/data/data-upload#supported-source-files).

## Wait for processing and inspect failures

```bash
unitlab batch-queue wait PROJECT_ID BATCH_QUEUE_ID --timeout 1800
unitlab batch-queue status PROJECT_ID BATCH_QUEUE_ID --json
unitlab batch-queue data PROJECT_ID BATCH_QUEUE_ID --json
```

The wait command always prints JSON and does not take a `--json` flag. For the valid two-image example, the final status should show:

```json
{
  "status": "completed",
  "total": 2,
  "completed": 2,
  "processing": 0,
  "failed": 0
}
```

The output can include additional fields. Check that `batch-queue data` contains `road.jpg` and `crossing.jpg`. A terminal queue may still contain failed items; inspect `failed` as well as `processing`. A waiting timeout does not cancel processing.

Upload returns a nonzero exit code for per-file upload failures. Keep the successful items and retry only corrected failures. The wait command reports processing results, so a script should also check its JSON `failed` count.

To find a queue later:

```bash
unitlab batch-queue list PROJECT_ID --json
unitlab batch-queue detail PROJECT_ID BATCH_QUEUE_ID --json
```

## Import media from connected storage

```bash
unitlab project import-cloud PROJECT_ID CLOUD_STORAGE_ID incoming/ --json
```

Multiple paths can follow the storage ID. Paths are relative to the saved connection prefix. Directory paths end with `/` and select immediate files; select child directories separately. Include `--fps` when the selection contains video. Save the returned `batch_queue_id` and check processing as above. See [CLI cloud storage](https://docs.unitlab.ai/api-sdk-cli/clis/cli-ontologies-and-cloud-storage).

## Attach existing data

Choose a folder or published dataset already available in your workspace. Run the preview first, check the selected item count, and then run the corresponding attachment command. These commands reuse existing data; they do not upload your local `images/` folder.

{% tabs %}
{% tab title="Folder" %}

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

{% endtab %}

{% tab title="Latest dataset version" %}

```bash
unitlab project attach PROJECT_ID --dataset DATASET_ID --preview --json
unitlab project attach PROJECT_ID --dataset DATASET_ID --json
```

{% endtab %}

{% tab title="Exact dataset version" %}

```bash
unitlab project attach PROJECT_ID --dataset-version DATASET_ID:3 --preview --json
unitlab project attach PROJECT_ID --dataset-version DATASET_ID:3 --json
```

{% endtab %}
{% endtabs %}

Use published dataset versions. Supply `--fps` if video is included. If attachment returns a Batch Queue ID, wait for it before importing labels or starting dependent work. See [datasets and versions](https://docs.unitlab.ai/api-sdk-cli/clis/cli-datasets-and-versions).

## LiDAR scene preview and upload

```bash
unitlab project upload PROJECT_ID --source ./recordings \
  --type pointcloud --dry-run --json

unitlab project upload PROJECT_ID --source ./recordings \
  --type pointcloud --json
```

The dry run lists detected scenes, frame counts, sensors, cameras and unused files without uploading. `--dry-run` and `--split-sensor-folders` require `--type pointcloud`. They are not general flags for image uploads.

When matching sensor folders represent independent recordings, add `--split-sensor-folders`. For scene folders, ZIPs, calibration and camera requirements, see [LiDAR preparation](https://docs.unitlab.ai/documentation/annotations/lidar-annotation#prepare-lidar-data).

## Inspect Data Units and sources

```bash
unitlab project data-units PROJECT_ID --status annotate --json
unitlab project data-unit PROJECT_ID DATA_UNIT_ID --json
unitlab project sources PROJECT_ID --json
```

Data Units are the project's work items: loose media or Data Groups. Their workflow status is separate from upload processing status. Related work commands are documented under [CLI workflow tasks](https://docs.unitlab.ai/api-sdk-cli/clis/cli-workflow-tasks).

Preview the effect before detaching a source:

```bash
unitlab project detach-source PROJECT_ID SOURCE_LINK_ID --preview --json
unitlab project detach-source PROJECT_ID SOURCE_LINK_ID --json
```

To rename a project, use `unitlab project update PROJECT_ID --name "Road scenes v2" --json`. To delete an entire project, resolve its exact ID first, then use `unitlab project delete PROJECT_ID`.

## JSON lists and annotation files

The CLI does not accept a generic file-list JSON through `--source` or an upload `--data-file` flag. Use a local script to read and validate such a list, then upload each path. The [SDK file-list example](https://docs.unitlab.ai/api-sdk-cli/sdks/project-uploads#use-a-local-json-file-list) explains the separate Batch Queues this creates.

Import labels through the [Python SDK](/api-sdk-cli/sdks/annotation-imports.md) or [frontend](https://docs.unitlab.ai/documentation/data/import-local-annotations) once media is ready. Use [Assets grouping configuration](https://docs.unitlab.ai/api-sdk-cli/clis/cli-assets-and-folders#group-related-files) when the JSON describes filename rules for related files.

Explore [Unitlab's data curation platform](https://unitlab.ai/en/data-curation).
