Multipart uploads

Use this guide when you need explicit control over upload chunks. For ordinary files, let your SDK select the upload mode.

Sequential uploads

Use multipart upload when the full file is inconvenient to send in one request. Send parts in order, one at a time.

The JavaScript SDK streams multipart bytes through POST /projects/:projectId/assets/multipart-upload for the first part and POST /assets/:assetId/part for later parts. The JSON/base64 equivalents shown below use POST /projects/:projectId/assets and the same flat continuation endpoint.

const uploading = await client.assets.create({
  name: "large-image.png",
  part: {
    uint8Array: firstPartBytes,
    type: "image/png",
    partNumber: 1,
    isFinal: false,
  },
});

const completed = await client.assets.uploadPart({
  assetId: uploading.id,
  part: {
    uint8Array: finalPartBytes,
    partNumber: 2,
    isFinal: true,
  },
});
  1. Start the upload with POST /projects/:projectId/assets.
  2. Send the first chunk in part.base64 with part.partNumber: 1.
  3. Continue with POST /assets/:assetId/part.
  4. Mark the last chunk with part.isFinal: true.

Multipart uploads currently expect:

  • Chunks provided as either base64 strings or int8Array number arrays.
  • Sequential partNumber values starting at 1.
  • The first request to include part.type for the full asset MIME type.
  • Optional totalSizeInBytes on the first request if you already know the size of the whole file. See Declaring the file size.
  • Non-final parts to be at least 5MiB; only the final part may be smaller.
  • A concrete file type. Ambiguous/container request MIME types such as application/octet-stream and multipart/* are rejected because CalmLens cannot infer how to process or store them as assets.

Raw ReadableStream parts must include their exact sizeInBytes. Buffer, Blob, base64, and byte-array payload sizes are calculated by the SDK.

If you use base64, split the original bytes first and then base64-encode each part. Do not split one full base64 string into substrings.

Multipart image uploads must be between 10MB and 20MB. If needed, an image may be re-encoded to a more efficient format during finalization.

Step 1: create the upload

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

const client = new CalmLensClient({
  apiKey: "your-api-key",
  projectId: "your-project-id"
});

const response = await client.assets.create({
  "name": "large-image.png",
  "part": {
    "base64": "your-part-1-base64",
    "type": "image/png",
    "partNumber": 1,
    "isFinal": false
  },
  "sizeInBytes": 7340032
});

The response returns an asset with status: "uploading" and an id you will use for the remaining parts.

Step 2: send more parts

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

const client = new CalmLensClient({
  apiKey: "your-api-key",
  projectId: "your-project-id"
});

const response = await client.assets.uploadPart({
  "part": {
    "base64": "your-part-2-base64",
    "partNumber": 2,
    "isFinal": false
  },
  "assetId": "your-asset-id"
});

Step 3: finalize the upload

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

const client = new CalmLensClient({
  apiKey: "your-api-key",
  projectId: "your-project-id"
});

const response = await client.assets.uploadPart({
  "part": {
    "base64": "your-final-part-base64",
    "partNumber": 3,
    "isFinal": true
  },
  "assetId": "your-asset-id"
});

The final part completes the upload and returns the asset. If a part number is skipped or repeated, the API returns 409. If the temporary upload is no longer open, the part endpoint also returns 409.

Queue mode

Choose queue when you start the upload. CalmLens saves the choice with the upload and applies it when the final part arrives, so the final part can come from another process or a restarted client that only knows the asset ID. Leave queue off later parts to use the saved choice, or pass it on the final part to override it for that upload.

Uploads started without queue are classified synchronously when they finish, like other uploads with queue=false. Audio and video are always processed asynchronously.

Declaring the file size

Pass the size of the whole file as totalSizeInBytes on the first request when you know it. CalmLens then:

Without totalSizeInBytes, CalmLens checks the accumulated size as parts arrive and the final total when the upload completes.

For raw uploads, sizeInBytes is the size of the request body: the first part on POST /projects/:projectId/assets/multipart-upload and each later part on POST /assets/:assetId/part.

Retrying parts

Parts are sequential, so a part sent twice is refused with 409 upload/invalid-part-number, and its details.expectedPartNumber tells you which part CalmLens needs next. If a request times out, retry it with the same part number: a part that already arrived is refused and you can move on, and a part that didn't arrive is accepted.

The final part closes the upload, so a retried final part normally returns 409 upload/not-open even though the upload succeeded. Send an Idempotency-Key header with the first request and with each part to get the original response back instead. See Retrying requests safely.

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