Files API
buildingEvery field a file response carries, and which ones can be absent.
The file object
The same shape everywhere a file is returned.
1{2 "id": "E7iVnWA1LiEpu3u8dRTVK",3 "projectId": "WXm71CLq69pZAZCWR2l_m",4 "ownerUserId": null,5 "filename": "sneaker-front-dEPY6g.jpg",6 "originalFilename": "sneaker front.jpg",7 "key": "files/originals/public/…/sneaker-front-dEPY6g.jpg",8 "contentType": "image/jpeg",9 "size": 204800,10 "metadata": null,11 "visibility": "public",12 "uploadStatus": "confirmed",13 "createdAt": "2026-08-14T13:38:29.830Z",14 "updatedAt": "2026-08-14T13:38:33.412Z",15 "transformations": { "image": { "enabled": true, "visibility": "public" } },16 "entity": { "type": "product", "id": "prod_abc123", "role": "gallery", "position": 0 },17 "url": "https://cdn…/files/originals/public/…",18 "variants": [{ "width": 100, "url": "…" }],19 "srcset": "…100w, …300w, …"20}| Field | Notes |
|---|---|
id | Ours. Stable for the file's life |
projectId | Always your own project |
ownerUserId | Whoever you said uploaded it, or null |
filename | Sanitised, with a uniqueness suffix |
originalFilename | What the user called it — display this |
key | The storage key. Returned so you can recognise your own objects; you never need to build one |
contentType | As declared at upload |
size | Bytes |
visibility | public or private |
uploadStatus | Always confirmed in list responses |
transformations | What was asked for, not what exists |
entity | The attachment, or null |
url | Permanent when public, signed for five minutes when private |
variants | Absent when the file has no sizes |
srcset | Absent for the same reason |
What can be absent
variants and srcset appear only for a transform-enabled image in a format we
can resize. Their absence is information — check for it rather than assuming:
1<img src={file.url} srcSet={file.srcset} sizes="(max-width: 768px) 100vw, 400px" />srcSet={undefined} is valid and the browser falls back to src, so the same
markup works for a PDF, an SVG and a photograph.
Pick sizes with sizes, not by choosing a width yourself. Hard-coding one means
shipping a 1200px image to a phone, or a 100px one to a retina display — the
browser knows both facts and you do not.
If you are not rendering to a browser — a PDF, an email, a native canvas — use
variants[], which carries the same widths as the srcset:
1const width = file.variants?.find((v) => v.width >= target);2const url = width?.url ?? file.url;transformations means requested, not built
transformations.image.enabled records what you asked for at upload. It does
not promise the sizes exist — each is generated the first time that width is
requested. variants is the field that tells you which ones are on offer.