The AssetResponse
The JSON every SDK reads: the HTML picture model as data, with a field for each attribute, descriptor or media feature it maps to.
The contract is plain JSON, objects, strings, numbers and arrays, with no polymorphism, so Swift decodes it with synthesized Codable, Kotlin with kotlinx.serialization, .NET with System.Text.Json and the web with a plain type. Every field is named after the HTML attribute, srcset descriptor or CSS media feature it maps to. Optional fields are left out, never null. A field the contract gains later is ignored by older clients.
An example
Brua's own bridge, read from the edge with ?lang=en, trimmed to a few of its variants:
{
"id": "bridge",
"version": 1,
"release": 3,
"url": "https://cdn.brua.dev/assets/991ceed8-a70e-4a5f-b3d3-e0b6b3d93e44/bridge",
"width": 640,
"height": 400,
"alt": "Brua, a bridge drawn as an arc over a line",
"lang": "en",
"variants": [
{ "type": "application/x-lottie+json", "prefersColorScheme": "light", "lang": "en", "url": ".../bridge/1/en-light.json" },
{ "type": "application/x-lottie+json", "prefersColorScheme": "dark", "lang": "en", "url": ".../bridge/1/en-dark.json" },
{ "type": "image/avif", "prefersColorScheme": "light", "prefersReducedMotion": "reduce", "lang": "en", "scale": 2, "url": ".../bridge/1/en-light-still@2x.avif" },
{ "type": "image/webp", "prefersColorScheme": "dark", "prefersReducedMotion": "reduce", "lang": "en", "scale": 2, "url": ".../bridge/1/en-dark-still@2x.webp" }
]
}The fields
| Field | Standard | Meaning |
|---|---|---|
id |
The asset's id, as the backend passes it on | |
version |
The asset's version in this release | |
release |
The project's release this JSON comes from; on the edge only, not in the delivery API's response | |
url |
HTTP content negotiation | The single URL: one URL that the edge resolves from the request, or from ?w= and the other overrides |
width, height |
<img width height> |
The size the asset is designed for: scale 1 for fixed assets, the source's size for flexible ones. Also the aspect ratio of every variant |
alt |
<img alt> |
Always present, in the language resolved from Accept-Language or ?lang=; "" only for decorative assets |
lang |
<img lang>, BCP 47 |
The language of alt; absent when decorative |
variants[].type |
IANA media type | image/avif, image/webp, image/png, image/jpeg, image/svg+xml, application/x-lottie+json, application/x-rive |
variants[].url |
The file, immutable under its versioned URL | |
variants[].prefersColorScheme |
CSS prefers-color-scheme |
light or dark; absent, the file fits both |
variants[].prefersReducedMotion |
CSS prefers-reduced-motion |
reduce marks the still of an animation, also its loading placeholder |
variants[].lang |
BCP 47 | For assets with files per language; only the resolved language's variants are returned |
variants[].scale |
srcset x |
A fixed asset's raster at that scale; 2 and 3 always, 1 when made |
variants[].pixelWidth |
srcset w |
A flexible asset's raster at that width; a ladder of widths |
A raster variant has scale or pixelWidth, never both. Thumbnails of a flexible asset are its smaller widths, not separate assets; an art-directed thumbnail is its own asset.
The selection rule
Given the variants and what the client renders (the media types it decodes, its color scheme, whether it reduces motion, its scale and the width it will show), the rule picks one variant. Every SDK and the edge run the same rule against the shared fixtures in the brua-web repository's fixtures/selection, so a change to it starts with a fixture.
Next
- The single URL: the headers and overrides behind
url. - Delivery API: the same JSON from the API, with a key.