Skip to main content

Custom fields

List your organization's custom fields with GET /v1/custom-fields and the options of a choice field, and write an asset's values with PATCH /v1/assets/{asset_id}/custom-fields, in the shape each type gives them.

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: 42 is not a text, and "true" is not a boolean.
  • A link’s label may be left out when writing; when reading, it is null where the address is shown as it is.
  • A date written outside the API may also carry a time of day, YYYY-MM-DDTHH:MM:SS, with an optional offset: read both forms.
  • record, json and file fields 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 id of 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_id that is not a choice field’s answers 404.

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.
  • null empties a field — so does an empty string on a text or a long_text, and an empty list on a multiple field. A required field 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_fields lists only the fields that apply to the asset and of which it holds a value; record, json and file fields are not in it yet.
  • updated_at does 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