Resources
Upload an asset
Upload an image as base64 so it can be used by image variables.
POST /api/assetsRequires assets:create. Returns 201 with the asset, including its id.
Request body
| Field | Required | Description |
|---|---|---|
workspaceId | Yes | Workspace id or slug. Note the field name differs from other endpoints. |
contentHash | Yes | A hash that identifies the bytes. Use the SHA-256 hex digest of the image. The server does not check it against the bytes. If the workspace already holds a file under this hash, that file is kept and the bytes you send are not stored again. |
imageBytes | Yes | The image, base64 encoded. |
filename | No | Display name. Defaults to asset-<contentHash>. |
contentType | No | image/png, image/jpeg, image/jpg, image/webp, image/gif, image/avif or image/bmp. Any other type returns 400 with Unsupported image content type. Defaults to image/png. |
collectionId | No | Image collection to add the image to. A collection that is not in the workspace is skipped. |
curl --request POST "https://app.kalpana.one/api/assets" \
--header "x-api-key: $KALPANA_API_KEY" \
--header "content-type: application/json" \
--data '{
"workspaceId": "acme",
"contentHash": "'"$(shasum -a 256 logo.png | cut -d' ' -f1)"'",
"imageBytes": "'"$(base64 < logo.png | tr -d '\n')"'",
"filename": "logo.png",
"contentType": "image/png"
}'Response
{
"id": "0b0f5a3e-5b1c-4d65-9a5e-3f7d2f2f6c11",
"workspaceId": "ws_123",
"contentHash": "…",
"storageKey": "…",
"filename": "logo.png",
"contentType": "image/png",
"sizeBytes": 20480,
"meta": { "width": 512, "height": 512 },
"uploadedBy": "user_123",
"status": "ready",
"createdAt": "2026-09-01T10:00:00.000Z",
"updatedAt": "2026-09-01T10:00:00.000Z",
"url": "https://cdn.example/temporary-url"
}Uploading a contentHash the workspace already holds returns the existing asset. Its name changes only when you send a filename. url is a temporary link.
Limits
- 5 MB per image. The image must be 5 MB or smaller, otherwise the request fails with
400and a message giving its size. Images are stored as uploaded, without re-encoding; resize it or export JPEG or WebP and try again. - Image types. PNG, JPEG, WebP, GIF, AVIF, and BMP. SVG is not accepted. AVIF is converted to PNG because the renderer cannot decode it, and the 5 MB limit applies to the PNG.
- No domain whitelist. The whitelist applies only to Upload from a URL, which fetches the file for you. Direct uploads send the bytes, so any source is fine.
- Request size. The bytes travel as base64 inside a JSON body, which is about a third larger than the file. For anything near the limit, use Upload assets directly, or host the image on a whitelisted domain and use Upload from a URL.
Was this page helpful?
Updated 4 Nov 2026