Lewati ke konten utama

Custom Fields (Developer)

This guide explains how custom fields are defined, stored, and managed at the API level. For the user-facing documentation, see the Custom Fields user guide.

Field Definition Schema​

Each board has a fields array defining the available field types. A field definition looks like this:

interface FieldDefinition {
id: string; // Unique identifier (e.g., "fld_abc123")
name: string; // Display name (e.g., "Priority")
type: FieldType; // One of the supported field types
required: boolean; // Whether the field is required
defaultValue?: any; // Default value for new items
description?: string; // Help text
options?: FieldOption[]; // For select-type fields
config?: Record<string, any>; // Type-specific configuration
}

Supported Field Types​

type FieldType =
| "short_text"
| "long_text"
| "email"
| "url"
| "phone"
| "number"
| "currency"
| "percentage"
| "rating"
| "status"
| "single_select"
| "multi_select"
| "priority"
| "date"
| "datetime"
| "timeline"
| "duration"
| "person"
| "people"
| "checkbox"
| "file"
| "label"
| "formula"
| "dependency"
| "mirror"
| "auto_number"
| "created_at"
| "updated_at";

Storage​

Field values are stored as a JSONB column on the items table:

CREATE TABLE items (
id TEXT PRIMARY KEY,
board_id TEXT NOT NULL REFERENCES boards(id),
group_id TEXT NOT NULL REFERENCES groups(id),
title TEXT NOT NULL,
description TEXT,
fields JSONB NOT NULL DEFAULT '{}',
position INTEGER NOT NULL DEFAULT 0,
created_by TEXT NOT NULL REFERENCES users(id),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

The fields JSONB column stores a key-value map where keys are field IDs and values depend on the field type:

{
"fld_status": "in_progress",
"fld_priority": "high",
"fld_assignee": "usr_abc123",
"fld_tags": ["backend", "auth"],
"fld_duedate": "2026-03-25",
"fld_estimate": 8,
"fld_done": true
}

Adding Fields via API​

board.addField​

POST /trpc/board.addField
{
"boardId": "brd_abc123",
"name": "Estimate",
"type": "number",
"required": false,
"config": {
"unit": "hours",
"min": 0,
"max": 100
}
}

board.updateField​

POST /trpc/board.updateField
{
"boardId": "brd_abc123",
"fieldId": "fld_abc123",
"name": "Story Points",
"config": {
"unit": "points"
}
}

board.removeField​

POST /trpc/board.removeField
{
"boardId": "brd_abc123",
"fieldId": "fld_abc123"
}

Removing a field deletes the field definition and removes the corresponding key from all items' fields JSONB.

Formula Fields​

Formula fields compute their value based on other fields. The formula syntax supports:

{fld_estimate} * {fld_rate} // Arithmetic
IF({fld_status} = "done", "Yes", "No") // Conditionals
DAYS_BETWEEN({fld_start}, {fld_end}) // Date functions
SUM({sub_items.fld_estimate}) // Aggregation over sub-items

Formula values are computed on read, not stored. They are recalculated each time the item is fetched.

Indexing​

For frequently filtered fields, PostgreSQL GIN indexes are created on the JSONB column:

CREATE INDEX idx_items_fields ON items USING GIN (fields);
CREATE INDEX idx_items_status ON items ((fields->>'fld_status'));

This ensures efficient queries even with large numbers of items and complex filters.