Custom fields
Custom fields have two halves:
- Definitions — a workspace-level catalog (
GET /custom_fields) describing each field: itsid,nameandkind. - Values — carried on a task, in its
custom_fields[]array, when you create (POST /tasks), update (PATCH /tasks/{id}) or read (GET /tasks/{id}) a task.
You read the definition to learn a field’s kind, then send the matching value field on the task.
Where fields come from, and which ones a task can have
Section titled “Where fields come from, and which ones a task can have”Custom fields are defined once at the workspace level and then linked into individual projects through project settings in the Bordio web app. The API is read-only here: you cannot create a field, edit its options, or link it to a project over the API — workspace admins do that in the web UI.
What this means when you work with tasks:
- A task can only hold values for custom fields that are linked to its project.
- The exact set of fields you may set on a given task is whatever
GET /custom_fields?project_id=<the task's project_id>returns. Treat that list as the source of truth for “which fields can I update on this task”. - Setting a value for a field that is not linked to the task’s project is rejected with
422. - A task with no project (workspace-level) cannot have custom fields at all — see
project_requiredbelow.
So the workflow is always the same: read the project’s fields, then set values only for those fields on tasks in that project. If someone adds or removes a field for the project in the web app, that change is reflected in GET /custom_fields?project_id=... the next time you read it.
The definition
Section titled “The definition”curl "https://api.bordio.com/public/v1/custom_fields?project_id=proj_6594a1b2c3d4e5f6a7b8c9d0" \ -H "Authorization: Bearer brd_sk_live_..."{ "data": [ { "id": "cf_aaa", "name": "Story points", "kind": "number" }, { "id": "cf_bbb", "name": "Budget", "kind": "currency" }, { "id": "cf_ccc", "name": "Release", "kind": "dropdown", "allow_multiple": false, "options": [ { "id": "cf_option_x1", "value": "v1.0" }, { "id": "cf_option_x2", "value": "v2.0" } ] }, { "id": "cf_ddd", "name": "Owners", "kind": "people", "allow_multiple": true } ]}kindtells you which value field to use on a task (see the table).allow_multipleis present only fordropdownandpeople.optionsis present only fordropdown.- Pass
?project_id=proj_...to get the fields available in that project. Without it you get the full workspace catalog. A field must be available in a task’s project to be set on that task.
How values live on a task
Section titled “How values live on a task”On a task, each value is one item in custom_fields[]:
{ "custom_field_id": "cf_aaa", "kind": "number", "number_value": 8 }- On read (
GET /tasks/{id}) every item carrieskindand exactly one populated value field. Fields with no value are omitted from the array entirely. - On write (
POST/PATCH) sendcustom_field_id+ the one value field that matches the field’s kind. You do not need to sendkind— it is taken from the definition (if you send it, it is ignored).
Custom field values require the task to belong to a project. Setting them on a workspace-level task (no project_id) fails with 422 validation_error (project_required).
Value format by kind
Section titled “Value format by kind”| kind | Value field | Format |
|---|---|---|
text |
text_value |
string (≤ 10000 chars) |
number |
number_value |
number |
percent |
percent_value |
number, where 75 means 75% |
currency |
currency_value |
object { "currency": <ISO-4217>, "value": <number> } |
date |
date_value |
ISO-8601 datetime, or YYYY-MM-DD for date-only |
checkbox |
checkbox_value |
boolean |
email |
email_value |
string (≤ 320) |
phone |
phone_value |
string (≤ 40) |
url |
url_value |
string (≤ 2048) |
dropdown |
dropdown_value |
array of option ids (cf_option_...) |
people |
people_value |
array of user ids (user_...) |
The sections below show the write payload and the read shape for each kind.
text, email, phone, url
Section titled “text, email, phone, url”Plain strings. Differ only in their max length and intent (email/phone/url are not format-validated by the API — store what you send).
// write{ "custom_field_id": "cf_txt", "text_value": "Needs design review" }{ "custom_field_id": "cf_eml", "email_value": "ops@acme.com" }// read{ "custom_field_id": "cf_txt", "kind": "text", "text_value": "Needs design review" }number
Section titled “number”Any number (integer or decimal). No unit.
// write{ "custom_field_id": "cf_pts", "number_value": 8 }// read{ "custom_field_id": "cf_pts", "kind": "number", "number_value": 8 }percent
Section titled “percent”A percent value is a plain number representing the percentage itself — 75 means 75%, not 0.75. Decimals are allowed (33.5 → 33.5%).
// write — set progress to 75%{ "custom_field_id": "cf_prog", "percent_value": 75 }// read{ "custom_field_id": "cf_prog", "kind": "percent", "percent_value": 75 }currency
Section titled “currency”A currency value is an object, not a number:
{ "currency": "<ISO-4217 code>", "value": <number> }currency— an ISO-4217 code such asUSD,EUR,GBP(required).value— the numeric amount (optional; omit it to record a currency without an amount).
// write — set budget to 1990.50 USD{ "custom_field_id": "cf_budget", "currency_value": { "currency": "USD", "value": 1990.5 }}// read{ "custom_field_id": "cf_budget", "kind": "currency", "currency_value": { "currency": "USD", "value": 1990.5 }}ISO-8601 datetime, or YYYY-MM-DD for a date without a time. On read, a date-only value comes back as YYYY-MM-DD; a datetime comes back as a full ISO-8601 timestamp.
// write — date-only{ "custom_field_id": "cf_due", "date_value": "2026-07-01" }// write — datetime{ "custom_field_id": "cf_due", "date_value": "2026-07-01T09:00:00.000Z" }// read{ "custom_field_id": "cf_due", "kind": "date", "date_value": "2026-07-01" }checkbox
Section titled “checkbox”A boolean.
// write{ "custom_field_id": "cf_billable", "checkbox_value": true }// read{ "custom_field_id": "cf_billable", "kind": "checkbox", "checkbox_value": true }dropdown
Section titled “dropdown”An array of option ids (cf_option_...) taken from the field’s options in its definition. Send a single-element array unless the definition has allow_multiple: true.
// write — single select{ "custom_field_id": "cf_release", "dropdown_value": ["cf_option_x1"] }// write — multi select (only if allow_multiple){ "custom_field_id": "cf_release", "dropdown_value": ["cf_option_x1", "cf_option_x2"] }// read{ "custom_field_id": "cf_release", "kind": "dropdown", "dropdown_value": ["cf_option_x1"] }Every option id must belong to that field, otherwise the request fails with 422 validation_error (custom_field_invalid_option).
people
Section titled “people”An array of user ids (user_...). Each must be a workspace member. Single-element unless the definition has allow_multiple: true.
// write{ "custom_field_id": "cf_owners", "people_value": ["user_abc", "user_def"] }// read{ "custom_field_id": "cf_owners", "kind": "people", "people_value": ["user_abc", "user_def"] }Worked example: set several fields on a task
Section titled “Worked example: set several fields on a task”curl -X PATCH https://api.bordio.com/public/v1/tasks/task_6a2918cff58b0a1d387866ee \ -H "Authorization: Bearer brd_sk_live_..." \ -H "Content-Type: application/json" \ -d '{ "custom_fields": [ { "custom_field_id": "cf_prog", "percent_value": 75 }, { "custom_field_id": "cf_budget", "currency_value": { "currency": "USD", "value": 1990.5 } }, { "custom_field_id": "cf_release","dropdown_value": ["cf_option_x1"] } ] }'Reading the task back:
// GET /tasks/task_6a2918cff58b0a1d387866ee{ "data": { "id": "task_6a2918cff58b0a1d387866ee", "title": "Ship the API", "custom_fields": [ { "custom_field_id": "cf_prog", "kind": "percent", "percent_value": 75 }, { "custom_field_id": "cf_budget", "kind": "currency", "currency_value": { "currency": "USD", "value": 1990.5 } }, { "custom_field_id": "cf_release", "kind": "dropdown", "dropdown_value": ["cf_option_x1"] } ] }}Clearing values
Section titled “Clearing values”Because custom_fields is replace-on-write, leave a field out of the array to clear it — but remember you must include every other field you want to keep.
// task currently has cf_prog, cf_budget, cf_release.// This PATCH keeps cf_prog and cf_budget, and clears cf_release:{ "custom_fields": [ { "custom_field_id": "cf_prog", "percent_value": 75 }, { "custom_field_id": "cf_budget", "currency_value": { "currency": "USD", "value": 1990.5 } } ]}To clear all custom fields, send "custom_fields": [].
Validation errors
Section titled “Validation errors”| Situation | HTTP | code |
|---|---|---|
| Custom fields set on a task with no project | 422 | project_required |
| Dropdown option id not defined on that field | 422 | custom_field_invalid_option |
| Field not available in the task’s project | 422 | validation_error |
| People value referencing a non-member | 422 | validation_error |
All carry the standard error envelope — see Errors.