Format & Quality
A stored JPEG does not have to leave as a JPEG. The same download URL that resizes can also transcode, and by default it does so on its own — a modern browser gets WebP from a PNG original without anyone asking.
Explicit Format
GET /api/buckets/photos/files/{key}?format=webp| Value | Output | Use it for |
|---|---|---|
webp |
image/webp |
Strong compression, effectively universal today. |
jpeg |
image/jpeg |
Photos where nothing may be assumed about the client. |
png |
image/png |
Transparency and lossless output. |
origin |
source | Opt out of transcoding for this request. |
The response Content-Type always reflects what was actually produced. An unrecognised value — bmp, tiff — answers 400.
AVIF is not available. Sovrium's image pipeline runs on the Bun runtime's own encoders, and those carry no AV1 encoder on Linux — the platform the binary and the Docker image run on. An AVIF option would therefore have worked on some machines and failed on others, so format=avif answers 400 like any other unsupported value. WebP is the modern format on offer: it compresses nearly as well, keeps transparency, and every browser released since 2020 reads it.
Automatic Negotiation
Omit format and the server reads the request's Accept header:
GET /api/buckets/photos/files/{key}
Accept: image/webp,image/*Accept contains |
Output |
|---|---|
image/webp |
WebP |
| Otherwise | The original bytes, untouched |
This is why an ordinary <img src> with no query string still gets a modern format in a modern browser and the untouched original in an old one — no <picture> element, no srcset juggling, no server-side user-agent sniffing.
format=origin is the way to bypass negotiation deliberately: use it when a downstream consumer needs the exact stored bytes, or when you are debugging what was actually uploaded.
Two pipelines, two rules. The file.transformImage automation action encodes to WebP when it converts and names no outputFormat; a plain resize there keeps the source format. This route ignores both rules: it negotiates from Accept and honours an explicit format, nothing else. There is no environment variable that changes either one.
Quality
GET /api/buckets/photos/files/{key}?quality=95
GET /api/buckets/photos/files/{key}?width=100&quality=30| Behaviour | Detail |
|---|---|
| Accepted range | Integer, 1 to 100 inclusive. |
| Default | 80 when omitted. |
| Applies to | Lossy output — JPEG and WebP. |
| Ignored for | PNG, which is lossless. |
| Outside the range, or non-integer | 400 |
Eighty is not a placeholder — it is close to the point where further quality stops being visible and starts only being bytes. Raising it to 95 can double the payload for a difference most viewers will not see on most images.
The pairing worth remembering is width with a low quality. A 100-pixel-wide thumbnail at quality=30 is a fraction of a full-quality one and looks identical at that size, because compression artefacts are themselves scaled away. Reserve high quality for images that will be viewed large.
Choosing in Practice
Three defaults that cover almost every case:
- Content images in a page — no
formatat all. Let negotiation do it; you get WebP where it helps and correctness everywhere else. - Thumbnails and avatars —
?width=…&quality=60. The size does the work; quality is nearly free to lower. - Downloads a user asked for —
?format=origin. Someone clicking "download original" means it.
Related Pages
- Resize & Fit — dimensions and the two fit modes.
- Presets & Caching — bundling these parameters under a name.
- File Operations — the endpoint being decorated.
- Ecoconception — the platform-wide environmental posture and its levers.
- Environment Variables: Services — the
ECO_*reference.
Last updated September 1, 2026
This documentation was written with AI, so errors or outdated content are possible. Sovrium is in beta. Contributions and corrections are welcome.