Custom fields are the ones your organization added to its library: what an asset may carry beside its title and its description — a useful link, a format, a shooting date, a speaker. Each organization has its own, and the API describes them before it writes them:
| Request | What it does | Permission |
|---|---|---|
GET /v1/custom-fields |
Lists the organization’s fields, page by page | assets.library.read |
GET /v1/custom-fields/count |
Counts them | assets.library.read |
GET /v1/custom-fields/{field_id}/choices |
Lists the options of a choice field | assets.library.read |
GET /v1/custom-fields/{field_id}/choices/count |
Counts them | assets.library.read |
PATCH /v1/assets/{asset_id}/custom-fields |
Writes an asset’s values | assets.library.edit |
An asset’s values are read in the custom_fields of GET /v1/assets/{asset_id}.
List the fields
curl https://api.videas.com/v1/custom-fields \
-H "Authorization: Bearer $VIDEAS_API_KEY"
{
"data": [
{
"id": "0192a680-1d2e-7f3a-8b4c-5d6e7f8a9b0c",
"label": "Useful link",
"slug": "useful-link",
"type": "link",
"multiple": false,
"required": false,
"public": true,
"workspace_id": null,
"asset_types": null,
"created_at": "2026-03-02T10:00:00Z",
"updated_at": "2026-03-02T10:00:00Z"
},
{
"id": "0192a680-2e3f-7a4b-9c5d-6e7f8a9b0c1d",
"label": "Format",
"slug": "format",
"type": "choice",
"multiple": false,
"required": false,
"public": true,
"workspace_id": null,
"asset_types": ["video", "third_party"],
"created_at": "2026-03-02T10:04:31Z",
"updated_at": "2026-06-18T14:20:07Z"
}
],
"next": null
}
| Field | |
|---|---|
id |
The field’s identifier — the one a write names it by, and the one to store. |
label |
What a person reads beside its value. |
slug |
A short handle the organization gave it. Not unique: two groups of fields may each hold one under the same handle. Always name a field by its id. |
type |
What it holds, which decides the shape of its value — see below. |
multiple |
For a choice field, whether it takes several options rather than one; false for every other type. |
required |
Whether it is mandatory: its value can be changed, never emptied. An asset may still have none yet. |
public |
Whether it is shown to the public wherever an asset carrying it is published — on a channel’s page and in the player’s details. |
workspace_id |
The one workspace whose assets it applies to; null for every workspace. |
asset_types |
The types of asset it applies to; null for every type. |
The list reads oldest first — order=desc for the reverse — and is walked with
next and after, like
every list.
Where a field applies
A field applies to an asset when its workspace and its type are the ones it
targets: workspace_id is null or the asset’s workspace, and asset_types is
null or holds the asset’s type. Writing a field that does not apply to the asset
is refused with a 400.
The types, and the shape of their value
A value has the shape its field’s type gives it — the same read and
written:
type |
Value | For example |
|---|---|---|
text |
a string, on one line | "Room B, level 2" |
long_text |
a string, on several lines | "First part…\nSecond part…" |
number |
a number | 42.5 |
date |
a day, YYYY-MM-DD |
"2026-11-02" |
boolean |
true or false |
true |
choice |
an option’s id; on a multiple field, a list of ids |
"0192a681-3f4a-7b5c-8d6e-7f8a9b0c1d2e" |
link |
{"url", "label"}: an absolute http or https address, and the words that stand for it |
{"url": "https://example.com/report", "label": "The full report"} |
record, json, file |
not read or written through the API yet | — |
- Nothing is converted:
42is not a text, and"true"is not a boolean. - A
link’slabelmay be left out when writing; when reading, it isnullwhere the address is shown as it is. - A
datewritten outside the API may also carry a time of day,YYYY-MM-DDTHH:MM:SS, with an optional offset: read both forms. record,jsonandfilefields are listed, but their values are neither read nor written here for now.- The list of types may grow: read a type you do not know as a field your client neither reads nor writes.
The options of a choice field
A choice field’s value holds option ids, not their labels.
GET /v1/custom-fields/{field_id}/choices lists a field’s options — apart,
because a field may offer thousands:
curl https://api.videas.com/v1/custom-fields/0192a680-2e3f-7a4b-9c5d-6e7f8a9b0c1d/choices \
-H "Authorization: Bearer $VIDEAS_API_KEY"
{
"data": [
{ "id": "0192a681-3f4a-7b5c-8d6e-7f8a9b0c1d2e", "label": "Report" },
{ "id": "0192a681-4a5b-7c6d-9e7f-8a9b0c1d2e3f", "label": "Interview" },
{ "id": "0192a681-5b6c-7d7e-8f8a-9b0c1d2e3f4a", "label": "Conference" }
],
"next": null
}
- Read this list to find the
idof an option you know by its label, and the label an asset’s value stands for. - It reads oldest first, which is not the order a form shows them in: sort them yourself if you show them.
- An option withdrawn from the field is not listed, and cannot be chosen any
more. An asset that chose it before keeps it, reads it back by its
id, and may be sent it again. - A
field_idthat is not achoicefield’s answers404.
To go from label to id, walk the options once and keep the table:
import os
import requests
API = "https://api.videas.com/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['VIDEAS_API_KEY']}"}
def option_ids(field_id):
"""{label: id} for every option of the field."""
ids, params = {}, {}
while True:
page = requests.get(f"{API}/custom-fields/{field_id}/choices", headers=HEADERS, params=params)
page.raise_for_status()
body = page.json()
ids.update({option["label"]: option["id"] for option in body["data"]})
if body["next"] is None:
return ids
params["after"] = body["next"]
Write an asset’s values
PATCH /v1/assets/{asset_id}/custom-fields takes a list of values, each naming
its field by its id:
curl -X PATCH https://api.videas.com/v1/assets/0192a6e4-3b5c-7d6e-8f90-a1b2c3d4e5f6/custom-fields \
-H "Authorization: Bearer $VIDEAS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"values": [
{
"field_id": "0192a680-1d2e-7f3a-8b4c-5d6e7f8a9b0c",
"value": { "url": "https://example.com/carbon-fibre-recycling", "label": "The full report" }
},
{
"field_id": "0192a680-2e3f-7a4b-9c5d-6e7f8a9b0c1d",
"value": "0192a681-3f4a-7b5c-8d6e-7f8a9b0c1d2e"
}
]
}'
- The fields you name take the values you send; the others keep theirs. It is not a list that replaces everything.
nullempties a field — so does an empty string on atextor along_text, and an empty list on amultiplefield. Arequiredfield cannot be emptied:400.- Each value is held to its field’s rules — a text’s length, a number’s bounds, an option the field still offers.
- At most 200 values per request, a field named once, and at most 256 KiB per value.
- All or nothing: if a single value is refused, none is written.
The answer is the whole asset; its values are in custom_fields, in the order a
form lays them out:
{
"id": "0192a6e4-3b5c-7d6e-8f90-a1b2c3d4e5f6",
"updated_at": "2026-10-15T08:30:14Z",
"custom_fields": [
{
"field_id": "0192a680-2e3f-7a4b-9c5d-6e7f8a9b0c1d",
"slug": "format",
"label": "Format",
"type": "choice",
"value": "0192a681-3f4a-7b5c-8d6e-7f8a9b0c1d2e"
},
{
"field_id": "0192a680-1d2e-7f3a-8b4c-5d6e7f8a9b0c",
"slug": "useful-link",
"label": "Useful link",
"type": "link",
"value": { "url": "https://example.com/carbon-fibre-recycling", "label": "The full report" }
}
]
}
(an excerpt: the answer carries every field of the asset.)
custom_fieldslists only the fields that apply to the asset and of which it holds a value;record,jsonandfilefields are not in it yet.updated_atdoes not move when its values change.- Sending the values the asset already holds changes nothing, and the last request to arrive wins.
- The change is recorded in the asset’s history under the key’s name, and an asset
validated in the organization’s shared pool is refused with a
403— as for its words and its tags.
Errors
Every error has the same body — {"code": "…", "message": "…", "request_id": "…"}.
Branch on code; message is written for a human and may change.
| Status | code |
What happened |
|---|---|---|
400 |
validation |
Write: more than 200 values, or a field named twice; a field that does not apply to this asset; a record, json or file field; a value not of its type’s shape, past its field’s rules or larger than 256 KiB; an option the field does not offer; null on a required field. The message names the field, by its slug or its id, and nothing is written. Reads: an unknown parameter, a limit that is not a whole number, an after from elsewhere. |
401 |
unauthorized |
No key, or a key that is unknown, expired or revoked. |
403 |
forbidden |
The key does not carry assets.library.read (reads) or assets.library.edit (the write); or the asset is validated in the shared pool. |
404 |
not_found |
No asset GET /v1/assets lists has this id; no field of GET /v1/custom-fields has one of the field_ids — the message names it, and nothing is written; or, for the options, the field is not a choice field. |
415 |
unsupported_media_type |
The body is not sent as application/json. |
422 |
validation |
values missing or not a list, an entry without its field_id or its value, a field_id that is not a uuid, or a field the body does not list. |
429 |
rate_limited |
Too many requests: wait the number of seconds Retry-After gives. |
Next steps
- Edit an asset — its words and its tags.
- Publish on a channel — where
a
publicfield is shown to its audience. - The API reference, generated from its contract.