All field values for an asset
GET /assets/{id}/fields
Every field value set on the asset, with the mirrored columns
(title, description) merged in as their declared fields.
Filtered by field_definition.read_capability. A caller
without the effective capability for a field does not receive its
value, and the row is dropped rather than blanked. Effective means
the same thing it means on
GET /assets/{id}/field-composition: a global holding, or one
scoped to the asset’s team where the asset has one.
Until #1173 this endpoint applied no per-field check at all, so
any authenticated caller received the values of every gated field
on the asset. That is closed. A caller that needs to distinguish
“withheld” from “never set” asks the field-composition read,
which answers exactly that and carries no values.
Authorizations
Section titled “Authorizations ”Parameters
Section titled “ Parameters ”Path Parameters
Section titled “Path Parameters ”Responses
Section titled “ Responses ”Map of field code -> typed value record
One field value as seen on an asset.
object
Where this value came from. default means an upload
default put it here and nothing has improved on it since —
extraction is free to overwrite it, and a human editing the
field replaces it with manual. It is the one provenance a
client cannot claim: AssetFieldValueWrite.set_by has no
default member, because a value a caller chose to send is
by definition not one nobody chose.
mirror means the field declares mirrors_column and this
value IS the asset column — nothing was stored under the
field. It is not a claim about who last edited it, because
the column carries no such record; set_at is the asset’s
updated_at and set_by_user_ref is always null. Like
default it is server-only: a caller cannot send it.
Display data for the vocabulary slugs this value holds, keyed by slug. Present only for select / multi_select / tree, and only for slugs that resolve against the field’s options document — a slug with no entry (the vocabulary dropped it, or the value predates it) is simply absent, and callers must fall back to rendering the slug itself.
asset_field_value stores the slug and never the label (ADR 0012) precisely so relabelling a term rewrites nothing. The cost of that indirection is that a reader has to resolve; this map is where the server pays it, once, for every consumer, so no display surface has to hold the field definition just to print a value.
object
One controlled-vocabulary term as a reader needs to see it.
Note there is no value — the map key is the slug.
object
Display text. Equal to the slug when the term carries no explicit label, which is the case for every option written in the bare-string form (see ADR 0012’s 2026-07-30 correction) — so a caller can always render this directly.
The term’s lifecycle state. A reader should mark anything other than active, so a value stops looking current the moment an operator retires the term behind it.
Ancestor labels from the root of the field’s vocabulary down
to and including this term — what a tree value needs in
order to display as “Europe / United Kingdom / London” when
the stored value is nothing but london.
Absent for any term that sits at the top level, which is
every term in a flat select / multi_select vocabulary: a
one-element path would say nothing label does not. So a
caller renders path.join(...) when it is present and
label when it is not.
The asset a reference value points at, as a reader needs to
see it. Present only for reference values whose value_ref
resolves to an asset this caller may see.
Absent is meaningful and load-bearing: it means the target
did not resolve — soft-deleted, or a dangling ref to a row that
no longer exists. A client MUST fall back to rendering the bare
value_ref UUID and MUST NOT treat the absence as an error.
Degrading to the id keeps the panel intact and leaks nothing:
the id was already on the record the caller is reading.
This mirrors resolved_options exactly, and for the same
reason (#775): the row carrying the value already joins the row
carrying the display text, so the server pays the resolution
once for every consumer rather than letting each display
surface print a raw UUID.
object
The target asset’s id. Equal to the enclosing value’s
value_ref — repeated here so a client holding a
resolved_reference never has to reach back out of it to
build the /assets/{id} link.
The target asset’s title. May be the empty string —
assets.title defaults to '' and nothing requires an
uploader to set one — so a client renders the id when this
is blank rather than an empty link.
Authentication required, missing, or invalid
object
Human-readable error summary
Example
the request could not be completedExample
{ "error": "authentication required: sign in and retry with a valid session or API token"}Resource not found
object
Human-readable error summary
Example
the request could not be completed