Organising assets

Use folders to organise content and scope access, metadata to attach structured information, and external IDs to connect assets to your own records.

External IDs

Use a stable externalId for your application record. A duplicate ID within a project returns 409 asset/duplicate-external-id. Folders and moves do not change asset IDs or CDN URLs.

Asset metadata

Attach custom metadata as an array of { id, value } items. To use a value for portal filtering or grouping, first define a field with the same ID in Portal › Configuration › Metadata. The field title and type control how the item appears in the portal; uploaded values remain JSON values and are not checked against the field type.

const asset = await client.assets.create({
  name: "campaign-image.jpg",
  content: { buffer: imageBytes, type: "image/jpeg" },
  metadata: [{ id: "campaign.id", value: "launch-2026" }],
});

For POST /projects/:projectId/assets, include the same metadata array in the JSON body. For the direct binary and streamed multipart endpoints, pass the JSON array as the URL-encoded metadata query parameter; the JavaScript SDK handles this encoding. Manage field definitions in Portal › Metadata fields.

Folders

Folders organize assets hierarchically within a project without changing asset IDs or CDN URLs. Like files in a filesystem, folders can contain assets and nested subfolders. Pass a folderId when you upload to put the new asset straight into a folder. See the Folders API reference for complete folder endpoint references and schemas.

  • Stable IDs: Folders and assets are identified by immutable UUIDs. Folder names and paths are display values and cannot be used as resource identifiers in API calls.
  • Nesting: Create folders with POST /projects/:projectId/folders at the project root (parentId: null or omitted) or nested under an existing folder by passing its parentId.
  • Renaming: Rename a folder with PATCH /folders/:folderId.
  • Moving: Move a folder with POST /folders/:folderId/move and { "parentId": string | null }, and an asset with POST /assets/:assetId/move and { "folderId": string | null }. Pass null to move a folder or asset to the project root.
  • Name uniqueness: Folder names must be unique among siblings sharing the same parent folder (or at the root). The same folder name can be used in different branches of the folder tree.
  • Hierarchy cycles: Moving a folder into itself or one of its descendants is rejected with 409 folder/parent-cycle.
  • Safe deletion: Only empty folders can be deleted. Attempting to delete a folder that still contains assets or child folders is rejected with 409 folder/not-empty.
const parent = await client.folders.create({ name: "Campaigns" });
const child = await client.folders.create({ name: "Launch", parentId: parent.id });

await client.assets.move({ assetId: asset.id, folderId: child.id });
await client.folders.move({ folderId: child.id, parentId: null });

To manage the folder tree itself:

import { CalmLensClient } from "@calmlens/sdk";

const client = new CalmLensClient({
  apiKey: process.env.CALMLENS_API_KEY!,
  projectId: process.env.CALMLENS_PROJECT_ID!,
});

// 1. Create a root folder
const marketing = await client.folders.create({
  name: "Marketing",
});

// 2. Create a nested child folder
const campaigns = await client.folders.create({
  name: "Campaigns",
  parentId: marketing.id,
});

// 3. List all folders in the project
const folders = await client.folders.list();

// 4. Retrieve a folder by ID
const folder = await client.folders.retrieve({
  folderId: campaigns.id,
});

// 5. Rename a folder
const updated = await client.folders.update({
  folderId: campaigns.id,
  name: "Summer Campaigns",
});

// 6. Move an asset into a folder
await client.assets.move({
  assetId: "987fcdeb-51a2-43d7-b890-123456789abc",
  folderId: campaigns.id,
});

// 7. Move an asset back to the project root
await client.assets.move({
  assetId: "987fcdeb-51a2-43d7-b890-123456789abc",
  folderId: null,
});

// 8. Move a folder under another parent or to root
await client.folders.move({
  folderId: campaigns.id,
  parentId: null, // Move to root
});

// 9. Delete an empty folder
await client.folders.delete({
  folderId: campaigns.id,
});

Find assets

See Pagination and filtering to query assets and Members and permissions to restrict access by folder.

Signing in

Signing out

Cookies on CalmLens

We use essential cookies to keep you signed in and the site secure. With your permission, we'll also use functional cookies to remember preferences such as your layout and documentation language, and to offer Google One Tap sign-in. We don't use analytics or advertising cookies. Manage cookies