Upload presets

An upload preset is a saved set of upload settings with its own ID. Browsers and apps upload through it without an API key, and your server can upload through it too, so every upload of that kind follows the same rules. Manage presets in Portal › Upload presets. For a step-by-step setup, see the quick start.

Who can upload

Each preset accepts browser uploads in one of two ways:

AccessHow a browser uploadsUse it for
Users your server approves (token)With an upload token your server signed for that uploadApps with sign-in
Anyone with the preset ID (anyone)With the preset ID alone, up to the preset's daily limitStatic sites, public forms and prototypes

New presets require a token. A preset open to anyone must have a daily upload limit.

Settings

SettingEffect
File typesMIME types the preset accepts, such as image/* or video/mp4. Empty accepts every supported type.
Maximum file sizeLargest file the preset accepts. Multipart uploads are checked as their parts arrive.
FolderFolder that uploads go into. Browser uploads can't choose another one.
VisibilityVisibility of uploaded assets.
Keep content after moderationKeeps each uploaded file so you can serve it from the CDN. When off, the file is deleted once it's moderated and only its record and report remain.
Read text in imagesRuns OCR on uploaded images and moderates the text it finds as well.
Show the decision to the uploaderWhen on, browser uploads see status for their own upload. Reports, metadata and URLs are never returned to the browser.
Daily upload limitMost browser uploads per UTC day. Uploads from your server don't count against it.
Allowed sitesOrigins allowed to upload from the browser, such as https://example.com. Requests from other sites, or without an Origin header, are refused.
Accept uploadsTurn off to pause the preset without deleting it.

A browser can only name and describe its upload. The folder, visibility, metadata, processing options and externalId come from the preset or the upload token; sending them from the browser fails with upload-preset/field-not-allowed.

Upload tokens

An upload token lets one browser upload through a preset that requires tokens. Sign tokens on your server with the JavaScript, Python, Go, Java, Ruby or Rust SDK. Signing happens locally with your API key and makes no request to CalmLens.

SDKMethodOptions
JavaScriptclient.uploadTokens.signuploadPresetId, externalId, expiresInSeconds
Pythonclient.upload_tokens.signupload_preset_id, external_id, expires_in_seconds
Goclient.UploadTokens.SignUploadPresetID, ExternalID, ExpiresInSeconds
Javaclient.uploadTokens.signuploadPresetId, externalId, expiresInSeconds
Rubyclient.upload_tokens.signupload_preset_id:, external_id:, expires_in_seconds:
Rustclient.upload_tokens().signupload_preset_id, external_id, expires_in_seconds

Each returns the token and when it expires, in milliseconds since the Unix epoch.

const { token, expiresAt } = await client.uploadTokens.sign({
  uploadPresetId: "your-upload-preset-id",
  externalId: "post-42",
  expiresInSeconds: 600,
});

Each token:

  • uploads one asset. A second upload with it fails with upload-token/used; an upload that fails frees the token for a retry.
  • starts an upload until it expires, and stays valid for that upload's later parts and status checks for up to a day.
  • acts for the API key that signed it. The key must be able to create assets in the preset's folder. Revoking the key stops its tokens working.

Uploading from a browser

Only the JavaScript SDK runs in browsers and apps. Its CalmLensAppClient, from @calmlens/sdk/app, uploads through an upload preset instead of an API key:

<script type="module">
  import { CalmLensAppClient } from "@calmlens/sdk/app";

  const client = new CalmLensAppClient({
    uploadPresetId: "your-upload-preset-id",
    token: () => fetchTokenFromYourServer(), // only for presets that require a token
  });
</script>

This client has only assets.create, assets.moderate, assets.uploadPart and assets.retrieve, for its own uploads. They return the asset's id, name, uploadPresetId and createdAt, plus status when the preset shows decisions. It refuses an API key with sdk/invalid-config.

Over HTTP, browser uploads use the same bodies and query parameters as the project upload endpoints, under the preset's path, with the token as Authorization: Bearer <token>:

RequestPath
Upload a filePOST /v1/upload-presets/{uploadPresetId}/assets/upload
Start a multipart uploadPOST /v1/upload-presets/{uploadPresetId}/assets/multipart-upload
Upload the next partPOST /v1/upload-presets/{uploadPresetId}/assets/{assetId}/part
Check an uploadGET /v1/upload-presets/{uploadPresetId}/assets/{assetId}

Uploading from your server through a preset

Pass uploadPresetId (upload_preset_id in Python and Ruby, UploadPresetID in Go, upload_preset_id in Rust) to client.assets.create() with your API key, in any SDK, to apply a preset's folder, visibility, file types and size limit. You can still set externalId, metadata and processing options. A folder or visibility that differs from the preset's fails with upload-preset/field-not-allowed.

Errors

CodeMeaning
upload-preset/not-foundNo preset with this ID in the project.
upload-preset/disabledThe preset is paused.
upload-preset/token-requiredThe preset requires an upload token and none was sent.
upload-preset/content-type-not-allowedThe preset doesn't accept this file type.
upload-preset/too-largeThe file is larger than the preset allows.
upload-preset/daily-limit-reachedThe preset reached its daily limit. Retry-After says when the next UTC day starts.
upload-preset/origin-not-allowedThe site isn't in the preset's allowed sites.
upload-preset/field-not-allowedThe upload set a field the preset or token controls.
upload-token/invalidThe token is malformed, for another preset, or signed by a key that can't upload here.
upload-token/expiredThe token expired before the upload started.
upload-token/usedThe token already uploaded an asset.

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