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 /, 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
/at the project root (projects/ :projectId/ folders parentId: nullor omitted) or nested under an existing folder by passing itsparentId. - Renaming: Rename a folder with PATCH
/.folders/ :folderId - Moving: Move a folder with POST
/andfolders/ :folderId/ move { "parentId": string | null }, and an asset with POST/andassets/ :assetId/ move { "folderId": string | null }. Passnullto 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.