/api/v1/object/field/{objectSlug}/{fieldSlug}Update a field's settings.
Update a field's settings.
Send only the properties you change. The field's type cannot change.
When you send options, existing choices keep their ids when they match
by id, value or label, so records that hold them are not affected. A
choice you drop while records still hold it is kept as archived instead
of deleted. System fields cannot be renamed, change type, or be turned
off.
Turning a field off (is_active: false) while a board view groups by it
answers 409, unless you add force=true.
The response is the stored field, including storage details you should
not rely on; read GET /object/field/show/{objectSlug}/{fieldSlug} for
the stable shape.
Query parameters:
force—trueto turn the field off even when a board groups by it. Any other value counts asfalse.
Scope: object.update.
Errors:
400— the new name is taken,optionsordefault_valuedo not fit the type, the change is not allowed on a system field, or the object or field does not exist.409— a board groups by this field andforceis nottrue(code: FIELD_IN_USE_BY_VIEWS). The message names the boards.422— the body is invalid, for example alabelover 100 characters.
Request:
{
"label": "Pipeline stage",
"required": true
}
Response 200 (abridged):
{
"status": 200,
"message": "Field updated successfully",
"data": {
"id": 912,
"name": "stage",
"slug": "stage-k2j9h4g7",
"label": "Pipeline stage",
"icon": null,
"type": "select",
"required": true,
"default_value": null,
"options": {
"values": [
{
"id": "6a1f3c9e-2b7d-4e8a-9f0c-1d2e3f4a5b6c",
"value": "lead",
"label": "Lead",
"color": "#3B82F6"
}
]
},
"is_active": true,
"sort_order": 4,
"is_system": false,
"api_only": false,
"created_at": "2026-08-02T08:30:00.000Z",
"updated_at": "2026-09-14T10:00:00.000Z"
}
}
Send request
Use the documented inputs to call this endpoint directly.
Parameters
Values are applied to the request and the example on the right.
Request body
application/jsonrequiredLock this field's VALUE to API-origin writes (FRD API-only FR-101).
A definition-level toggle, gated by object.* like the rest of this DTO —
rejected on computed field types, which are already engine-owned.
Responses
Successful response
{
"id": 1.5,
"name": "string",
"slug": "string",
"label": "string",
"icon": "string",
"type": "text",
"required": true,
"default_value": "string",
"options": "string",
"is_active": true,
"sort_order": 1.5,
"column_name": "string",
"column_type": "string",
"is_system": true,
"api_only": true,
"relation_id": "",
"object": "string",
"group_id": "",
"workspace_id": "",
"created_at": "2026-01-01T00:00:00Z",
"updated_at": "2026-01-01T00:00:00Z"
}Values: "text" · "long_text" · "number" · "boolean" · "datetime" · "date" · "select" · "multi_select" · "rating" · "currency" · "email" · "link" · "phone" · "location" · "relation" · "file" · "whatsapp_number" · "tag" · "priority" · "status" · "start_date" · "due_date" · "source_name" · "session_name" · "provider_id" · "smart_catalog" · "serial_number" · "calculation" · "photo" · "lookup" · "rollup"
Physical PostgreSQL column name.
INVARIANT: this column is guaranteed to be a safe PostgreSQL identifier
— every write path passes the value through sanitizePgIdentifier()
before storing (see FieldCreateService, ObjectTemplateApplicator,
DataMigrationService, seeders). Callers that interpolate this value into
raw SQL do NOT need to re-sanitize it.
NULL for a NOT-STORED type (lookup), which owns no physical column.
The TS type stays string rather than string | null only because
widening it breaks ~40 call sites across unrelated modules that declare
their own column_name: string structural types; every read site that can
meet a lookup already guards with if (!field.column_name). Widening this
repo-wide is a worthwhile standalone refactor — do NOT assume non-null in
new code.
Value-write lock: when true only a write whose resolved origin is 'api'
may set this field's value; every other origin has it silently stripped
(FRD API-only FR-100/104). Orthogonal to is_system (which protects the
DEFINITION) and to required/default_value.
FK to the companion relations row for type='relation' fields.
NULL on non-relation fields. Backfilled by migration 1788000000007 from
the implicit field.name == relation.name join. NOT NULL is enforced
on relation-type fields by migration 1788000000008.
The field's section (FRD-34) on its HOME object. NULL ⇒ the implicit
"Ungrouped" section. Placed (shared) fields carry their own per-object
section on field_placements.group_id instead — mirroring how home
ordering lives on field.sort_order and placed ordering on
field_placements.position. FK ON DELETE SET NULL: deleting a group
only un-groups the field, never deletes it (FR-3412).
Bad request
{
"status": 1,
"message": "string",
"code": "string",
"data": {}
}The HTTP status code of the response.
What went wrong. Several validation messages are joined with ", ".
A stable, machine-readable reason. Only some errors set it.
Extra detail about the error. Only some errors set it.
Unauthorized
{
"status": 1,
"message": "string",
"code": "string",
"data": {}
}The HTTP status code of the response.
What went wrong. Several validation messages are joined with ", ".
A stable, machine-readable reason. Only some errors set it.
Extra detail about the error. Only some errors set it.
Internal server error
{
"status": 1,
"message": "string",
"code": "string",
"data": {}
}The HTTP status code of the response.
What went wrong. Several validation messages are joined with ", ".
A stable, machine-readable reason. Only some errors set it.
Extra detail about the error. Only some errors set it.
Powered by heyo-docs