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:
| Setting | Value |
|---|---|
| Daily upload limit | Required. 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 size | Only what your page expects, such as image/* up to 10 MB. Anything else is refused before it's stored. |
| Allowed sites | Your 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 uploader | On, so your page can tell people whether their upload was accepted. |
| Keep content after moderation | On 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:
| Status | What the uploader sees |
|---|---|
| allowed or limited | Their file was accepted. |
| age-restricted | Their file was accepted and is shown only to people who can see sensitive content. |
| pending | Their file was uploaded and is still being checked. The widget checks for up to 15 seconds. |
| blocked or purged | Their 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
externalIdthat 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.