Lenses and transforms
Use reusable named lenses or one-off transform selectors to resize and convert retained images through the CalmLens CDN. Transformations produce derived responses without creating new asset records. CDN URLs use https://cdn.calmlens.com/v1.
Choose a transform
| Need | Use |
|---|---|
| A reusable thumbnail or image preset | A named lens. Update its version when its definition changes. |
| A one-off size or format | A direct transform selector in the URL. |
Named lenses
Lenses are named CDN transforms. They let you request a derived version of an asset without creating another asset record. Lens and transform selectors work on both /content and /preview. See Delivering assets for retention, visibility, authorization, and availability requirements.
Use a named lens by appending /@:lensIdentifier to the CDN URL. The @ prefix makes the selector unambiguously a named lens.
/assets/:projectId/:assetIdentifier/content/@thumbnailExample:
const response = await client.cdn.serve({
assetIdentifier: assetId,
lens: "thumbnail",
});
When signing a private lens URL, include the same lens identifier in the signing request:
import { CalmLensClient } from "@calmlens/sdk";
const client = new CalmLensClient({
apiKey: "your-api-key",
projectId: "your-project-id"
});
const response = await client.assets.createSignedUrl({
"resource": "content",
"lens": "thumbnail",
"expiresInSeconds": 900,
"assetId": "your-asset-id"
});
The CDN validates the signature against the lens, then applies it.
Direct transforms
For one-off derived assets, append a direct transform selector without @. The selector uses the existing transform syntax and can contain one or more comma-separated transforms.
/assets/:projectId/:assetIdentifier/content/imageTransform[format=png,width=1024]const response = await client.cdn.serve({
assetIdentifier: assetId,
transform: "imageTransform[format=png,width=1024]",
});
Direct transforms are not saved as lenses. Use a versioned lens when a transform should have a stable project-owned name and cache invalidation on definition changes.
Image transform options
The supported transform type is imageTransform. Options are comma-separated key=value pairs inside brackets.
| Option | Accepted values |
|---|---|
format | jpg, png, webp, avif, or gif |
width, height | Non-negative integers |
fit | scale-down, contain, pad, squeeze, cover, or crop |
quality | Integer from 0 through 255 |
rotate | 0, 90, 180, or 270 |
flip | h, v, or hv |
| Other adjustments | background, blur, brightness, contrast, gamma, saturation, sharpen, gravity, border, segment, and trim |
Unknown transform types and malformed selectors return 400 asset/invalid-transform. Unsupported options return 400 asset/invalid-transform-options.
Versioned custom lenses
Lenses are versioned, project-scoped definitions for reusable CDN transformations. Use them when a derived asset needs a stable URL and predictable cache invalidation.
Create or update a project lens with the SDK's idempotent client.ensureLens() method:
const normalLens = await client.ensureLens({
identifier: "picmagic-normal",
name: "PicMagic normal",
type: "image",
active: true,
version: 1,
transforms: [
{
type: "imageTransform",
options: { fit: "scale-down", width: 1024, format: "png" },
},
],
});
const response = await client.cdn.serve({
assetIdentifier: assetId,
resource: "content",
lens: normalLens.identifier,
});
Versioning rules:
- Create a new lens with any positive version.
- Repeating the same version and complete definition is idempotent, so it is safe to run on every deploy.
- Any other update must use a version greater than the current version; versions do not need to be consecutive.
- Reusing or lowering the current version for a different definition returns
409 lens/version-conflict. - Lens identifiers contain lowercase letters, numbers, and hyphens and are at most 50 characters.
To change the width or output format, submit the complete new definition with version 2. Always submit the complete definition when updating a lens. CalmLens includes the resolved lens version in its CDN cache and ETag identities, so a new version never serves a cached result from the prior definition. SDK lens URLs use the required @ prefix automatically.
To ensure multiple lenses in one request, use client.ensureLenses():
const [normalLens, uhdLens] = await client.ensureLenses({
lenses: [normalDefinition, uhdDefinition],
});
Use the single-lens endpoint for one definition or the bulk endpoint to ensure several definitions in one request. The endpoint details below document the complete REST request and response schemas.
Lens API reference
See the Lenses reference for objects, request schemas and endpoint details.