Assets
An asset is a file, remote URL, or extracted child submitted to CalmLens for classification, together with its moderation report. Use these endpoints to upload, list, update, move, retry and delete assets.
Guides: Uploading assets, Asset lifecycle, Delivering assets.
Wait for moderation or queue it
For uploads, queue=false (the HTTP default) waits for classification and returns the decision. Set queue=true to return an asset ID while moderation runs, then receive an asset.classified event through a webhook. Also handle asset.failed for processing failures.
The SDK moderate method selects queue=false; create selects queue=true. Text and images support both flows. Audio and video always run asynchronously and return pending, even with queue=false. See Uploading assets for the complete flow.
Endpoints
| Endpoint | Description |
|---|---|
| Upload | |
POST / | Upload a new asset to a project. |
POST / | Upload a file by sending its raw bytes as the request body, with the X-CalmLens-Upload: content header and the file's Content-Type. |
POST / | Start a multipart upload by streaming the first part's raw bytes as the request body, with the X-CalmLens-Upload: part header and the file's Content-Type. |
POST / | Upload a sequential part for an in-progress asset upload. |
| Read and review | |
GET / | Get a paginated list of assets for a project |
GET / | Search accessible assets by keyword or describe an image in natural language. |
GET / | Get a single asset by ID |
GET / | Get child assets of a parent asset |
POST / | Process an asset that failed (status error) again. |
| Organise | |
PATCH / | Update an existing asset |
POST / | Move an asset into a folder, or to the project root |
| Remove and batch | |
DELETE / | Remove stored content, including the content derived from it in child assets, while retaining the asset records |
DELETE / | Delete an asset, its child assets and their stored content from a project |
POST / | Queue a bounded batch operation for selected assets |
Endpoint details
Create Asset
Upload a new asset to a project. Direct image uploads must be smaller than 10MB. Images from 10MB through 20MB must use multipart upload; CalmLens accumulates parts and enforces the final total, and checks `totalSizeInBytes` up front when you send it. Retained multipart image originals are served through CalmLens CDN URLs. Send an `Idempotency-Key` header to retry safely without creating a second asset.
import { CalmLensClient } from "@calmlens/sdk";
const client = new CalmLensClient({
apiKey: "your-api-key",
projectId: "your-project-id"
});
const response = await client.assets.create({
"name": "homepage-image.jpg",
"url": "https://example.com/image.jpg",
"visibility": "private",
"processingOptions": {
"keepContentAfterProcessing": true
}
});
Response
Error Codes
auth/missing-tokenauth/invalid-api-keyauth/project-mismatchupload/file-too-largeupload/image-requires-multipart-uploadupload/multipart-image-too-smallupload/multipart-image-too-largeupload/limit-exceededasset/duplicate-external-idfolder/not-foundupload/invalid-mime-typeupload/invalid-payloadupload/use-upload-partidempotency/invalid-keyidempotency/key-reusedidempotency/request-in-progressUpload Asset File
Upload a file by sending its raw bytes as the request body, with the `X-CalmLens-Upload: content` header and the file's `Content-Type`. Asset fields are query parameters. This is the upload path the SDKs use for files. Send an `Idempotency-Key` header to retry safely without creating a second asset.
import { readFile } from "node:fs/promises";
const response = await fetch("https://api.calmlens.com/v1/projects/:projectId/assets/upload?name=cat.jpg&contentType=image%2Fjpeg&queue=true&visibility=private", {
method: "POST",
headers: {
"X-Api-Key": "your-api-key",
"X-CalmLens-Upload": "content",
"Content-Type": "image/jpeg",
},
body: await readFile("cat.jpg"),
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const data = await response.json();
Response
Error Codes
auth/missing-tokenauth/invalid-api-keyauth/project-mismatchupload/file-too-largeupload/image-requires-multipart-uploadupload/multipart-image-too-smallupload/multipart-image-too-largeupload/limit-exceededasset/duplicate-external-idfolder/not-foundupload/invalid-mime-typeupload/invalid-payloadidempotency/invalid-keyidempotency/key-reusedidempotency/request-in-progressStart Multipart File Upload
Start a multipart upload by streaming the first part's raw bytes as the request body, with the `X-CalmLens-Upload: part` header and the file's `Content-Type`. Pass `partNumber=1`, `isFinal`, the part's `sizeInBytes` and, if known, the file's `totalSizeInBytes` as query parameters, then send later parts to Upload Asset Part. The `queue` choice is saved with the upload and applies when the final part arrives.
const response = await fetch("https://api.calmlens.com/v1/projects/:projectId/assets/multipart-upload?name=large-video.mp4&contentType=video%2Fmp4&queue=true&partNumber=1&isFinal=false", {
method: "POST",
headers: {
"X-Api-Key": "your-api-key"
}
});
const data = await response.json();
Response
Error Codes
auth/missing-tokenauth/invalid-api-keyauth/project-mismatchupload/file-too-largeupload/image-requires-multipart-uploadupload/multipart-image-too-smallupload/multipart-image-too-largeupload/limit-exceededasset/duplicate-external-idfolder/not-foundupload/invalid-mime-typeupload/invalid-payloadidempotency/invalid-keyidempotency/key-reusedidempotency/request-in-progressUpload Asset Part
Upload a sequential part for an in-progress asset upload. Image multipart uploads must total from 10MB through 20MB; each non-final part must be at least 5MiB. The final part is classified with the `queue` choice saved when the upload started unless you pass `queue` here. To stream raw bytes instead of JSON, send the `X-CalmLens-Upload: part` header and pass `partNumber`, `isFinal` and `sizeInBytes` as query parameters.
import { CalmLensClient } from "@calmlens/sdk";
const client = new CalmLensClient({
apiKey: "your-api-key",
projectId: "your-project-id"
});
const response = await client.assets.uploadPart({
"assetId": "987fcdeb-51a2-43d7-b890-123456789abc",
"part": {
"base64": "<base64-encoded-part>",
"partNumber": 2,
"isFinal": false
}
});
Response
Error Codes
auth/missing-tokenauth/invalid-api-keyauth/project-mismatchasset/not-foundupload/file-too-largeupload/multipart-image-too-smallupload/multipart-image-too-largeupload/invalid-payloadupload/not-openupload/invalid-part-numberupload/missingupload/size-mismatchidempotency/invalid-keyidempotency/key-reusedidempotency/request-in-progressimport { CalmLensClient } from "@calmlens/sdk";
const client = new CalmLensClient({
apiKey: "your-api-key",
projectId: "your-project-id"
});
const response = await client.assets.list({
"pageIndex": 0,
"pageSize": 20
});
Response
Error Codes
auth/missing-tokenauth/invalid-api-keyauth/project-mismatchproject/not-foundfolder/not-foundSearch assets
Search accessible assets by keyword or describe an image in natural language. Semantic image search must be enabled in project settings. Results are ranked and limited to 20.
import { CalmLensClient } from "@calmlens/sdk";
const client = new CalmLensClient({
apiKey: "your-api-key",
projectId: "your-project-id"
});
const response = await client.assets.search({
"query": "a red car at night",
"mode": "hybrid",
"limit": 10
});
Response
disabledreadyindexingunavailableError Codes
auth/missing-tokenauth/invalid-api-keyauth/project-mismatchimport { CalmLensClient } from "@calmlens/sdk";
const client = new CalmLensClient({
apiKey: "your-api-key",
projectId: "your-project-id"
});
const response = await client.assets.retrieve({
"assetId": "987fcdeb-51a2-43d7-b890-123456789abc"
});
Response
Error Codes
auth/missing-tokenauth/invalid-api-keyauth/project-mismatchasset/not-foundproject/not-foundimport { CalmLensClient } from "@calmlens/sdk";
const client = new CalmLensClient({
apiKey: "your-api-key",
projectId: "your-project-id"
});
const response = await client.assets.listChildren({
"pageIndex": 0,
"pageSize": 20,
"assetId": "987fcdeb-51a2-43d7-b890-123456789abc"
});
Response
Error Codes
auth/missing-tokenauth/invalid-api-keyauth/project-mismatchRetry Asset
Process an asset that failed (status error) again. It returns to pending and is moderated like a new upload. Its content must still be stored.
import { CalmLensClient } from "@calmlens/sdk";
const client = new CalmLensClient({
apiKey: "your-api-key",
projectId: "your-project-id"
});
const response = await client.assets.retry({});
Response
Error Codes
auth/missing-tokenauth/invalid-api-keyauth/project-mismatchasset/not-foundasset/not-retryableasset/safety-heldconst response = await fetch("https://api.calmlens.com/v1/assets/987fcdeb-51a2-43d7-b890-123456789abc", {
method: "PATCH",
headers: {
"X-Api-Key": "your-api-key",
"Content-Type": "application/json"
},
body: JSON.stringify({
"name": "Updated asset name",
"visibility": "private"
})
});
const data = await response.json();
Response
Error Codes
auth/missing-tokenauth/invalid-api-keyauth/project-mismatchasset/not-foundasset/update-failedasset/duplicate-external-idimport { CalmLensClient } from "@calmlens/sdk";
const client = new CalmLensClient({
apiKey: "your-api-key",
projectId: "your-project-id"
});
const response = await client.assets.move({});
Response
Error Codes
auth/missing-tokenauth/invalid-api-keyauth/project-mismatchasset/not-foundasset/update-failedasset/safety-heldfolder/not-foundRemove Asset Content
Remove stored content, including the content derived from it in child assets, while retaining the asset records
import { CalmLensClient } from "@calmlens/sdk";
const client = new CalmLensClient({
apiKey: "your-api-key",
projectId: "your-project-id"
});
const response = await client.assets.deleteContent({
"assetId": "987fcdeb-51a2-43d7-b890-123456789abc"
});
Response
Error Codes
auth/missing-tokenauth/invalid-api-keyauth/project-mismatchasset/not-foundasset/update-failedimport { CalmLensClient } from "@calmlens/sdk";
const client = new CalmLensClient({
apiKey: "your-api-key",
projectId: "your-project-id"
});
const response = await client.assets.delete({
"assetId": "987fcdeb-51a2-43d7-b890-123456789abc"
});
Error Codes
auth/missing-tokenauth/invalid-api-keyauth/project-mismatchasset/not-foundconst response = await fetch("https://api.calmlens.com/v1/projects/:projectId/assets/batch", {
method: "POST",
headers: {
"X-Api-Key": "your-api-key",
"Content-Type": "application/json"
},
body: JSON.stringify({
"assetIds": [
"987fcdeb-51a2-43d7-b890-123456789abc"
],
"operation": "remove-content"
})
});
const data = await response.json();
Response
Error Codes
auth/missing-tokenauth/invalid-api-keyauth/project-mismatchproject/not-foundThe asset object
Every endpoint that returns an asset returns this object. report follows the Report format, and status moves through the asset lifecycle. Child assets that CalmLens extracts during processing, such as images in a document, are assets too: their parentId is the asset they came from, and Get Asset Children lists them.
The page object
List endpoints return a page of assets.
| Field | Type | Presence | Description |
|---|---|---|---|
pageIndex | number | Required | The zero-based page index. Default: 0. |
pageSize | number | Required | Number of items per page (1-100) |
ordering | object[] | Required | Array of field ordering specifications |
items | Asset[] | Required | Items on this page |
total | number | Required | Total number of matching items |
hasMore | boolean | Required | Whether another page follows this one |