Upload without a server

Let people upload from a static site, a public form or a prototype with nothing but an upload preset's ID. Files go straight to CalmLens, the uploader sees whether their file was accepted, and you review uploads in the portal. When you need to know who uploaded, or to show uploads to other visitors, upload with your server instead.

1. Create a preset open to anyone

In Portal › Upload presets, create a preset and choose Anyone with the preset ID. Anyone who loads your page can upload through it, so its settings are what protect your usage:

SettingValue
Daily upload limitRequired. The most uploads each visitor (IP address) makes per UTC day; further uploads fail with 429 upload-preset/daily-limit-reached.
File types and maximum sizeOnly what your page expects, such as image/* up to 10 MB. Anything else is refused before it's stored.
Allowed sitesYour site's origins, so other websites can't use the preset from a browser. Scripts outside a browser can still send uploads, so keep the daily limit. Leave it empty for mobile apps, which send no Origin header.
Show the decision to the uploaderOn, so your page can tell people whether their upload was accepted.
Keep content after moderationOn only if your page shows uploads back. When off, each file is deleted once it's moderated, and its record and report remain.

The preset ID is safe to put in your page: it can only upload through this preset, into its folder, with its settings. Upload presets explains every setting.

2. Add the upload widget

Install @calmlens/sdk in your front end and add the upload widget with the preset ID alone:

import { type CalmLensUpload, CalmLensUploader } from "@calmlens/sdk/react";

export function PublicPhotoUploader({
  onUploaded,
}: {
  onUploaded: (upload: CalmLensUpload) => void;
}) {
  return (
    <CalmLensUploader
      uploadPresetId="preset_4Lk8Qw2Xm9Rt5Vb3Nc7P"
      accept="image/*"
      maxFileSize={10 * 1024 * 1024}
      onUploaded={onUploaded}
    />
  );
}

In a React Native or Expo app, use the React Native uploader with the same settings. To build your own interface, create a CalmLensAppClient with the preset ID and call client.assets.classify(), which waits for the decision.

3. Show the uploader the decision

Each upload returns its asset ID, name and upload time, and its moderation status when the preset shows decisions. The widget turns the status into a message for the uploader:

StatusWhat the uploader sees
allowed or limitedTheir file was accepted.
age-restrictedTheir file was accepted and is shown only to people who can see sensitive content.
pendingTheir file was uploaded and is still being checked. The widget checks for up to 15 seconds.
blocked or purgedTheir file can't be used because it breaks your rules.

The browser never receives the moderation report, metadata or URLs, only the status of its own upload.

4. Show the upload back to the uploader

To show the uploader their file, turn on Keep content after moderation, set the preset's visibility to public, and render the media component with your project ID and the asset ID and status from onUploaded. It shows blocked and purged files as unavailable without requesting them.

By default the CDN serves a public file as soon as its upload completes, before moderation decides. Turn on Block assets until moderation finishes to serve only files that have passed moderation.

Only the uploader's browser knows the asset ID, and a browser can't list uploads, so other visitors can't find the file. Showing uploads to everyone needs somewhere to keep their IDs, which is a server.

5. Review uploads

Uploads appear in the preset's folder in the portal, with each one's moderation report. To hear about new uploads and decisions, connect Slack or email in Portal › Webhooks & connectors.

When to add a server

Move to uploading through your server when you need to:

  • Know who uploaded a file, or attach it to a record such as a post, with an externalId that only your server can set.
  • Show uploads to other visitors, which needs their asset IDs stored somewhere.
  • Limit uploads per signed-in user instead of per IP address.
  • Publish from a decision your server reads from CalmLens. The status in the browser is for the uploader's feedback only, because people can change what their browser sends.

Calmform is a static feedback form built this way.

Signing in

Signing out

Cookies on CalmLens

We use essential cookies for sign-in and security. With your permission, functional cookies remember your preferences and Google Analytics measures website use and signups. Optional ad measurement cookies link Google ad clicks to signups. Personalised advertising stays off. Manage cookies