Asset permissions

Keep assets private when access depends on your application's users, teams, subscriptions or sharing rules. Store the CalmLens asset ID alongside your own ownership and access records. CalmLens authenticates your project API key; your backend decides which of your users may access each asset. Your users do not need CalmLens accounts.

Project and folder permissions restrict what your backend's API key can access. They do not evaluate the identity of an end user signed in to your application. Keep that key on your server and grant only the permissions and folders your integration needs, including Download assets for private delivery.

Before choosing a delivery method, retain the content and configure its delivery eligibility.

Check access in your backend

Both delivery options start with the same permission check in your backend. Complete it before issuing a signed URL or fetching content to proxy:

  1. Authenticate the caller with your application's session or token.
  2. Look up the requested asset in your database, scoped to the caller's tenant and ownership or sharing rules. An asset ID supplied by the browser is not proof of access.
  3. Return an error without fetching or signing content if the caller cannot read it. A 404 can avoid revealing that another user's asset exists.
  4. Resolve the stored CalmLens asset ID and project from that authorised record, then use one of the delivery methods below.

Apply the same check to originals, previews, thumbnails, downloads and transformed versions. If a caller can choose a lens or transformation, validate that choice against the variants they may access; a thumbnail permission need not grant the original file.

Choose a delivery method

After the access check succeeds, deliver the permitted resource using one of these two options.

Short-lived signed URLAuthenticated proxy
Download pathClient downloads directly from CalmLensYour backend fetches and streams the file
Application permission checkBefore each link is issuedOn every request reaching your endpoint
Permission removed in your databaseExisting links remain valid until expiryThe next request can be denied, provided caches cannot bypass the check
Application-specific renderingUse CalmLens lenses and transformsCan also apply your own annotations or processing
Moderation and content availabilityCalmLens checks CDN requestsCalmLens checks upstream requests; your own byte cache needs invalidation

Prefer signed URLs for ordinary delivery when a short access window is acceptable. Use a proxy when you need to check application permissions on every request or render content in ways CalmLens does not provide.

Option 1: Issue a short-lived signed URL

After your backend grants access, create a signed URL and return it to the browser. The browser downloads directly from CalmLens, so your backend does not relay the file. Choose the resource and any lens or transforms on the server, and use a lifetime suitable for the content.

const signedUrl = await client.assets.createSignedUrl({
  assetId,
  resource: "content",
  expiresInSeconds: 900,
});

The response contains url and expiresAt. Return them to the client and request a new link through your backend when needed, repeating the permission check. Lifetimes range from 1 second to 24 hours, with a 15-minute default. See POST /assets/:assetId/signed-url for the full contract.

A signed URL is a bearer link: anyone holding it can use it until it expires, subject to CalmLens delivery eligibility. Your application server must check its own permission records before issuing each link. If you remove a user's access in your database, your server should refuse to issue them new links; CalmLens does not read that database or enforce those application permissions. Links already issued remain valid until they expire. Use short lifetimes, or the authenticated proxy when permission must be rechecked on every request.

Option 2: Proxy content through your application

Use an authenticated endpoint in your own backend when you want to recheck permission on every request. After the access check, fetch the private resource with your server SDK and stream its response to the caller:

const response = await client.cdn.serve({
  assetIdentifier: assetId,
});

Check the upstream status before returning its body, and preserve the appropriate content type. The browser receives your application's content endpoint, while the CalmLens API key stays on the server.

PolySnap uses this pattern: its asset-content route authenticates the caller, queries its project asset records by both asset ID and user ID, returns 404 if there is no owned asset, and only then calls CalmLens through a server-side content helper. That helper can also apply an application-selected lens or image edits.

Run authorisation before looking up a cached response, including cache hits. Keep any internal byte cache behind that check. For private responses sent to clients, use Cache-Control: private, no-store when each new request must reach your access check; do not forward public cache headers or allow a shared cache to serve your authenticated endpoint without authorisation. If your application caches content independently, you must also handle moderation changes, content removal and cache invalidation; CalmLens cannot check or revoke bytes served from your cache.

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