# Folders

Folders organize assets hierarchically within a project without changing asset IDs or CDN URLs. Folders and assets are always addressed by ID; names are display values, unique among siblings.

Guide: [Organising assets](/docs/guides/organising-assets#folders).

## Endpoints

| Endpoint | Description |
| --- | --- |
| [`GET /projects/:projectId/folders`](#list-folders) | List all asset folders in a project |
| [`POST /projects/:projectId/folders`](#create-folder) | Create an asset folder with an optional parent folder ID |
| [`GET /folders/:folderId`](#get-folder) | Get an asset folder by ID |
| [`PATCH /folders/:folderId`](#update-folder) | Rename an asset folder |
| [`DELETE /folders/:folderId`](#delete-folder) | Delete an empty asset folder by ID |
| [`POST /folders/:folderId/move`](#move-folder) | Move a folder under another folder, or to the project root |
| [`POST /assets/:assetId/move`](#move-asset) | Move an asset into a folder, or to the project root |

## The folder object

| Field | Type | Presence | Description |
| --- | --- | --- | --- |
| `id` | `string` | Required | Unique folder ID |
| `projectId` | `string` | Required | ID of the project that owns the folder |
| `parentId` | `string` | Optional, nullable | ID of the parent folder; `null` for folders at the project root |
| `name` | `string` | Required | Display name, unique among folders with the same parent. 1 to 100 characters, without slashes. |
| `createdAt` | `integer` | Required | When the folder was created, in milliseconds since the Unix epoch |
| `updatedAt` | `integer` | Optional | When the folder was last updated, in milliseconds since the Unix epoch |

## Endpoint details

### List Folders

List all asset folders in a project

**Endpoint:** `GET https://api.calmlens.com/v1/projects/:projectId/folders`

**Authentication:** API Key

#### Example request

```bash
curl -X GET "https://api.calmlens.com/v1/projects/{projectId}/folders" \
  -H "X-Api-Key: your-api-key"
```

#### Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `folders` | `array<Folder>` | Yes |  |

#### Error codes

| Status | Code | Description |
| --- | --- | --- |
| 401 | <a id="get-folders-error-auth-missing-token" />`auth/missing-token` | Authentication required. Send your project API key in the X-Api-Key header. |
| 401 | <a id="get-folders-error-auth-invalid-api-key" />`auth/invalid-api-key` | The API key is invalid, revoked or expired. Create a key under Developer → API Keys. |
| 403 | <a id="get-folders-error-auth-project-mismatch" />`auth/project-mismatch` | This API key belongs to a different project. Use a key created for the project that owns this resource. |
| 404 | <a id="get-folders-error-project-not-found" />`project/not-found` | Project not found |

***

### Create Folder

Create an asset folder with an optional parent folder ID

**Endpoint:** `POST https://api.calmlens.com/v1/projects/:projectId/folders`

**Authentication:** API Key

#### Example request

```bash
curl -X POST "https://api.calmlens.com/v1/projects/{projectId}/folders" \
  -H "X-Api-Key: your-api-key"
```

#### Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | `string` | Yes |  |
| `parentId` | `string \| null` | No |  |

#### Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `string` | Yes | Unique folder ID |
| `projectId` | `string` | Yes | ID of the project that owns the folder |
| `parentId` | `string \| null` | No | ID of the parent folder; `null` for folders at the project root |
| `name` | `string` | Yes | Display name, unique among folders with the same parent. 1 to 100 characters, without slashes. |
| `createdAt` | `integer` | Yes | When the folder was created, in milliseconds since the Unix epoch |
| `updatedAt` | `integer` | No | When the folder was last updated, in milliseconds since the Unix epoch |

#### Error codes

| Status | Code | Description |
| --- | --- | --- |
| 401 | <a id="post-folder-error-auth-missing-token" />`auth/missing-token` | Authentication required. Send your project API key in the X-Api-Key header. |
| 401 | <a id="post-folder-error-auth-invalid-api-key" />`auth/invalid-api-key` | The API key is invalid, revoked or expired. Create a key under Developer → API Keys. |
| 403 | <a id="post-folder-error-auth-project-mismatch" />`auth/project-mismatch` | This API key belongs to a different project. Use a key created for the project that owns this resource. |
| 404 | <a id="post-folder-error-project-not-found" />`project/not-found` | Project not found |
| 404 | <a id="post-folder-error-folder-not-found" />`folder/not-found` | Folder not found |
| 409 | <a id="post-folder-error-folder-duplicate-name" />`folder/duplicate-name` | A folder with this name already exists in this location. |

***

### Get Folder

Get an asset folder by ID

**Endpoint:** `GET https://api.calmlens.com/v1/folders/:folderId`

**Authentication:** API Key

#### Example request

```bash
curl -X GET "https://api.calmlens.com/v1/folders/{folderId}" \
  -H "X-Api-Key: your-api-key"
```

#### Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `string` | Yes | Unique folder ID |
| `projectId` | `string` | Yes | ID of the project that owns the folder |
| `parentId` | `string \| null` | No | ID of the parent folder; `null` for folders at the project root |
| `name` | `string` | Yes | Display name, unique among folders with the same parent. 1 to 100 characters, without slashes. |
| `createdAt` | `integer` | Yes | When the folder was created, in milliseconds since the Unix epoch |
| `updatedAt` | `integer` | No | When the folder was last updated, in milliseconds since the Unix epoch |

#### Error codes

| Status | Code | Description |
| --- | --- | --- |
| 401 | <a id="get-folder-error-auth-missing-token" />`auth/missing-token` | Authentication required. Send your project API key in the X-Api-Key header. |
| 401 | <a id="get-folder-error-auth-invalid-api-key" />`auth/invalid-api-key` | The API key is invalid, revoked or expired. Create a key under Developer → API Keys. |
| 403 | <a id="get-folder-error-auth-project-mismatch" />`auth/project-mismatch` | This API key belongs to a different project. Use a key created for the project that owns this resource. |
| 404 | <a id="get-folder-error-project-not-found" />`project/not-found` | Project not found |
| 404 | <a id="get-folder-error-folder-not-found" />`folder/not-found` | Folder not found |

***

### Update Folder

Rename an asset folder

**Endpoint:** `PATCH https://api.calmlens.com/v1/folders/:folderId`

**Authentication:** API Key

#### Example request

```bash
curl -X PATCH "https://api.calmlens.com/v1/folders/{folderId}" \
  -H "X-Api-Key: your-api-key"
```

#### Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `name` | `string` | No |  |

#### Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `string` | Yes | Unique folder ID |
| `projectId` | `string` | Yes | ID of the project that owns the folder |
| `parentId` | `string \| null` | No | ID of the parent folder; `null` for folders at the project root |
| `name` | `string` | Yes | Display name, unique among folders with the same parent. 1 to 100 characters, without slashes. |
| `createdAt` | `integer` | Yes | When the folder was created, in milliseconds since the Unix epoch |
| `updatedAt` | `integer` | No | When the folder was last updated, in milliseconds since the Unix epoch |

#### Error codes

| Status | Code | Description |
| --- | --- | --- |
| 401 | <a id="patch-folder-error-auth-missing-token" />`auth/missing-token` | Authentication required. Send your project API key in the X-Api-Key header. |
| 401 | <a id="patch-folder-error-auth-invalid-api-key" />`auth/invalid-api-key` | The API key is invalid, revoked or expired. Create a key under Developer → API Keys. |
| 403 | <a id="patch-folder-error-auth-project-mismatch" />`auth/project-mismatch` | This API key belongs to a different project. Use a key created for the project that owns this resource. |
| 404 | <a id="patch-folder-error-project-not-found" />`project/not-found` | Project not found |
| 404 | <a id="patch-folder-error-folder-not-found" />`folder/not-found` | Folder not found |
| 409 | <a id="patch-folder-error-folder-duplicate-name" />`folder/duplicate-name` | A folder with this name already exists in this location. |

***

### Delete Folder

Delete an empty asset folder by ID

**Endpoint:** `DELETE https://api.calmlens.com/v1/folders/:folderId`

**Authentication:** API Key

#### Example request

```bash
curl -X DELETE "https://api.calmlens.com/v1/folders/{folderId}" \
  -H "X-Api-Key: your-api-key"
```

#### Response

```json
{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}
```

#### Error codes

| Status | Code | Description |
| --- | --- | --- |
| 401 | <a id="delete-folder-error-auth-missing-token" />`auth/missing-token` | Authentication required. Send your project API key in the X-Api-Key header. |
| 401 | <a id="delete-folder-error-auth-invalid-api-key" />`auth/invalid-api-key` | The API key is invalid, revoked or expired. Create a key under Developer → API Keys. |
| 403 | <a id="delete-folder-error-auth-project-mismatch" />`auth/project-mismatch` | This API key belongs to a different project. Use a key created for the project that owns this resource. |
| 404 | <a id="delete-folder-error-project-not-found" />`project/not-found` | Project not found |
| 404 | <a id="delete-folder-error-folder-not-found" />`folder/not-found` | Folder not found |
| 409 | <a id="delete-folder-error-folder-not-empty" />`folder/not-empty` | Folder must be empty before it can be deleted. |

***

### Move Folder

Move a folder under another folder, or to the project root

**Endpoint:** `POST https://api.calmlens.com/v1/folders/:folderId/move`

**Authentication:** API Key

#### Example request

```bash
curl -X POST "https://api.calmlens.com/v1/folders/{folderId}/move" \
  -H "X-Api-Key: your-api-key"
```

#### Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `parentId` | `string \| null` | Yes |  |

#### Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `string` | Yes | Unique folder ID |
| `projectId` | `string` | Yes | ID of the project that owns the folder |
| `parentId` | `string \| null` | No | ID of the parent folder; `null` for folders at the project root |
| `name` | `string` | Yes | Display name, unique among folders with the same parent. 1 to 100 characters, without slashes. |
| `createdAt` | `integer` | Yes | When the folder was created, in milliseconds since the Unix epoch |
| `updatedAt` | `integer` | No | When the folder was last updated, in milliseconds since the Unix epoch |

#### Error codes

| Status | Code | Description |
| --- | --- | --- |
| 401 | <a id="move-folder-error-auth-missing-token" />`auth/missing-token` | Authentication required. Send your project API key in the X-Api-Key header. |
| 401 | <a id="move-folder-error-auth-invalid-api-key" />`auth/invalid-api-key` | The API key is invalid, revoked or expired. Create a key under Developer → API Keys. |
| 403 | <a id="move-folder-error-auth-project-mismatch" />`auth/project-mismatch` | This API key belongs to a different project. Use a key created for the project that owns this resource. |
| 404 | <a id="move-folder-error-folder-not-found" />`folder/not-found` | Folder not found |
| 409 | <a id="move-folder-error-folder-duplicate-name" />`folder/duplicate-name` | A folder with this name already exists in this location. |
| 409 | <a id="move-folder-error-folder-parent-cycle" />`folder/parent-cycle` | A folder cannot be moved inside itself or one of its descendants. |

***

### Move Asset

Move an asset into a folder, or to the project root

**Endpoint:** `POST https://api.calmlens.com/v1/assets/:assetId/move`

**Authentication:** API Key

#### Example request

```bash
curl -X POST "https://api.calmlens.com/v1/assets/{assetId}/move" \
  -H "X-Api-Key: your-api-key"
```

#### Request body

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `folderId` | `string \| null` | Yes |  |

#### Response

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `string` | Yes | Unique asset ID |
| `projectId` | `string` | Yes | ID of the project that owns the asset |
| `sizeInBytes` | `number,null` | No | Size of the uploaded content in bytes |
| `userId` | `unknown \| null` | No | ID of the user whose account created the asset |
| `parentId` | `string \| null` | No | ID of the parent asset for child assets CalmLens extracts during processing, such as images in a document or OCR text. `null` for uploaded assets. |
| `folderId` | `string \| null` | No | Folder containing the asset |
| `createdAt` | `integer` | Yes | When the asset was created, in milliseconds since the Unix epoch |
| `updatedAt` | `integer` | No | When the asset was last updated, in milliseconds since the Unix epoch |
| `checksums` | `object \| null` | No | Digests of the uploaded content |
| `imageData` | `object \| null` | No | Image properties, for image assets |
| `documentData` | `object \| null` | No | Document properties, for documents |
| `name` | `unknown` | Yes | Name of the asset |
| `fileFormat` | `unknown \| null` | No | MIME type of the stored content |
| `fileExtension` | `unknown \| null` | No | File extension of the stored content |
| `description` | `unknown \| null` | No | Optional description of the asset |
| `externalId` | `unknown \| null` | No | External identifier for the asset |
| `externalUrl` | `string \| null` | No | External URL associated with the asset |
| `previewImage` | `object \| null` | No | Browser-friendly preview generated for images, documents and websites |
| `storageId` | `unknown \| null` | No | Internal storage key of the retained content; `null` when nothing is stored |
| `uploadPartsReceived` | `integer \| null` | No | Number of parts received so far for a multipart upload |
| `type` | `image \| video \| audio \| plaintext-document \| multimedia-document \| website \| file` | Yes | Asset type, detected from the content's MIME type |
| `metadata` | `array<MetadataItem> \| null` | No | List of custom metadata items for the asset |
| `tags` | `array<unknown> \| null` | No | Array of tags for the asset |
| `report` | `object \| null` | No | Moderation report, set once classification finishes. See [Report format](/docs/guides/moderation-reports). |
| `moderationAction` | `ignore \| alert \| hide \| purge \| null` | No | The moderation action applied when this asset crossed a policy threshold |
| `status` | `uploading \| pending \| approved \| rejected \| error` | Yes | Processing and moderation status. See [Asset lifecycle](/docs/guides/asset-lifecycle). |
| `safetyHold` | `object \| null` | No | Internal delivery restriction for suspected illegal content. Held content cannot be viewed, changed, or deleted through normal asset operations. |
| `rejectionNote` | `unknown \| null` | No | Reason the asset was rejected, generated automatically or supplied by a reviewer |
| `extractedText` | `unknown \| null` | No | Extracted text stored inline for small content or in R2 for larger content |
| `processingOptions` | `object \| null` | No | Processing options supplied when the asset was uploaded |
| `visibility` | `public \| private \| null` | No | Visibility level of the asset |
| `storageDeletedAt` | `number,null` | No | When the stored content was removed, in milliseconds since the Unix epoch. The asset record and report remain. |

#### Error codes

| Status | Code | Description |
| --- | --- | --- |
| 401 | <a id="move-asset-error-auth-missing-token" />`auth/missing-token` | Authentication required. Send your project API key in the X-Api-Key header. |
| 401 | <a id="move-asset-error-auth-invalid-api-key" />`auth/invalid-api-key` | The API key is invalid, revoked or expired. Create a key under Developer → API Keys. |
| 403 | <a id="move-asset-error-auth-project-mismatch" />`auth/project-mismatch` | This API key belongs to a different project. Use a key created for the project that owns this resource. |
| 404 | <a id="move-asset-error-asset-not-found" />`asset/not-found` | Asset not found |
| 500 | <a id="move-asset-error-asset-update-failed" />`asset/update-failed` | Failed to update asset |
| 423 | <a id="move-asset-error-asset-safety-held" />`asset/safety-held` | Asset access is restricted by a safety hold. |
| 404 | <a id="move-asset-error-folder-not-found" />`folder/not-found` | Folder not found |
