Skip to content

Update a work item property

PATCH/api/v2/workspaces/{slug}/projects/{project_id}/work-item-properties/{pk}/

Change a property's label, description, default, requirement, or availability. Values already recorded on work items stay where they are — you are editing the field definition, not rewriting anyone's data.

PATCH is partial. Send only the fields you want to change; anything you omit keeps its current value. Omitting a field is not the same as sending null.

Path Parameters

slug:requiredstring

The workspace slug. It appears in your Plane URLs — in https://app.plane.so/my-team/projects/, the slug is my-team.

project_id:requiredstring (uuid)

The project the property belongs to.

pk:requiredstring (uuid)

The id of the property to update.

Body Parameters

Every field is optional — send the subset you are changing. There is no name field: name is the read-side slug, and display_name is what you write.

display_name:optionalstring

New human-readable label for the field. Maximum 255 characters.

description:optionalstring

Free-form explanation of what the field is for, shown as helper text.

property_type:optionalstring

The kind of data the field holds: TEXT, DATETIME, DECIMAL, BOOLEAN, OPTION, RELATION, URL, EMAIL, FILE, or FORMULA.

Changing the type of a property that already holds values changes what those values mean, so treat this as a migration rather than an edit — in most cases creating a new property and retiring the old one with is_active: false is the safer move. The full reference is on the properties overview.

relation_type:optionalstring

What a RELATION property points at: ISSUE, USER, RELEASE, or RICH_TEXT. Only meaningful when property_type is RELATION.

options:optionalarray of object

Write-only. Define OPTION choices inline, the same way you can on create. Each entry is a property option object with name required.

For adding, editing, or removing a single choice on a live property, prefer the dedicated endpoints under Property options — they let you address one option by id instead of restating the set. The response always returns the resolved options array, never the payload you sent.

is_multi:optionalboolean

Whether the field accepts more than one value. Turning it off on a property that already holds several values per work item is a narrowing change — check your data first.

is_required:optionalboolean

Whether a value must be present. This applies going forward: existing work items with the field empty are not rejected retroactively, but the next edit will ask for a value. Pair it with a default_value so automated flows have something to fall back on.

is_active:optionalboolean

Whether the property is offered. Setting false is the reversible way to retire a field — the definition, its options, and its recorded values all survive, and the field simply stops being offered.

default_value:optionalarray of string

The value applied when none is supplied. Always an array, even for a single-valued property — a DECIMAL field that defaults to 3 is sent as ["3"], not 3. Send [] to clear the default.

settings:optionalany

Free-form object holding type-specific configuration. The shape depends on property_type, so send the whole object rather than assuming keys — a partial settings object replaces the stored one.

validation_rules:optionalany

Free-form object holding type-specific validation constraints. Same whole-object caveat as settings.

external_id:optionalstring

Your system's identifier for this field, for sync and import correlation. Maximum 255 characters.

external_source:optionalstring

The system external_id came from, for example jira or linear. Maximum 255 characters.

Scopes

projects.work_item_properties:write

Errors

StatusCodeCause
400validation_errorAn enum value outside the list, or a field over its length limit.
401unauthorizedMissing or invalid credentials.
403forbiddenYour role or token scope can't update properties.
404resource_not_foundNo such property, project, or workspace — or it's outside your tenant.
409work_item_types_managed_at_workspaceThis workspace manages work item types at the workspace level. Update the property on the workspace endpoint instead.
429rate_limitedThrottled. Honor the Retry-After header before retrying.

A 409 means wrong surface, not missing permission

If the workspace manages work item types at the workspace level, this project endpoint returns 409 work_item_types_managed_at_workspace — the write belongs on Update a workspace work item property instead. Reading the same property through this path still works. See Work item type modes.

name is not writable

name is derived by Plane and returned on reads only. Renaming the field means sending a new display_name.

Update a work item property
bash
curl -X PATCH \
  "https://api.plane.so/api/v2/workspaces/my-team/projects/4af68566-94a4-4eb3-94aa-50dc9427067b/work-item-properties/c81b7e2a-5f34-4d90-8e17-3a6c9b0f2d75/" \
  -H "X-Api-Key: $PLANE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "display_name": "Story points",
  "is_required": true,
  "default_value": ["3"]
}'
Response200
json
{
  "id": "c81b7e2a-5f34-4d90-8e17-3a6c9b0f2d75",
  "name": "story_points",
  "display_name": "Story points",
  "description": "Relative sizing for planning",
  "property_type": "DECIMAL",
  "relation_type": null,
  "is_multi": false,
  "is_required": true,
  "is_active": true,
  "default_value": ["3"],
  "options": [],
  "settings": {},
  "validation_rules": {},
  "logo_props": {},
  "external_id": null,
  "external_source": null,
  "created_at": "2026-01-14T09:24:03.117482Z"
}
Response409
json
{
  "type": "https://api.plane.so/errors/conflict",
  "title": "Conflict",
  "status": 409,
  "code": "work_item_types_managed_at_workspace",
  "detail": "Work item types are managed at the workspace level for this workspace."
}

Retiring a field without losing data

Deleting a property is permanent. When you want a field to stop appearing but its history to stay readable, deactivate it:

json
{
  "is_active": false
}

The definition, its options, and every value already recorded stay in place, and setting is_active back to true brings the field back exactly as it was.