/api/v1/object/field/{objectSlug}Add a field to an object.
Add a field to an object.
Creates the field and its storage, and returns the saved field. The new
field gets a generated slug; use that slug, not name or label, as
the key in record data. name must be unique on the object, ignoring
letter case. options holds the settings of the field type, for example
the choices of a select field, and is checked against that type.
default_value is checked against the type too. api_only: true lets
only API keys and OAuth apps write the field's value.
Types relation, status and tag cannot be created here. 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.
Scope: object.create.
Errors:
400— a field with this name exists on the object, the type cannot be created here,optionsordefault_valuedo not fit the type,api_onlyis set on a computed type, or no active object has that slug.422— the body is invalid, for example an unknowntype.
Request:
{
"name": "stage",
"label": "Stage",
"type": "select",
"required": false,
"options": {
"values": [
{ "label": "Lead", "value": "lead", "color": "#3B82F6" },
{ "label": "Customer", "value": "customer", "color": "#10B981" }
]
}
}
Response 201 (abridged):
{
"status": 201,
"message": "Field created successfully",
"data": {
"id": 912,
"name": "stage",
"slug": "730418526901",
"label": "Stage",
"type": "select",
"required": false,
"default_value": null,
"options": {
"values": [
{
"label": "Lead",
"value": "lead",
"color": "#3B82F6",
"id": "6a1f3c9e-2b7d-4e8a-9f0c-1d2e3f4a5b6c"
},
{
"label": "Customer",
"value": "customer",
"color": "#10B981",
"id": "b2c4d6e8-1a3b-4c5d-8e7f-9a0b1c2d3e4f"
}
]
},
"is_active": true,
"api_only": false,
"sort_order": 9,
"is_system": false,
"created_at": "2026-09-14T10:00: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/jsonrequiredValues: "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"
Lock 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