Create a workspace work item property
Define a custom property once for the whole workspace. display_name and property_type are the only required fields; everything else refines how the value is captured.
A new property is not live yet
Creating a property adds it to the workspace catalog. It appears on work items only once a context binds it to projects and work item types. Follow this call with Create a property context, or the property will sit in the catalog unused.
Wrong mode is a 409, not a 404
If the workspace manages work item types at the project level, this route returns 409 with the code work_item_types_managed_at_project. Create the property on the project-level resource instead. See Work item type modes.
Path Parameters
slug:requiredstringThe workspace slug. It appears in your Plane URLs — in https://app.plane.so/my-team/projects/, the slug is my-team.
Body Parameters
display_name:requiredstringThe label shown wherever the property is rendered. Maximum 255 characters.
property_type:requiredstringWhat kind of value the property holds. Choose carefully — it governs what relation_type, options, settings, and validation_rules mean.
TEXT— free-form textDATETIME— a date and timeDECIMAL— a numberBOOLEAN— true or falseOPTION— a choice from a defined listRELATION— a pointer to another record; pair it withrelation_typeURL— a linkEMAIL— an email addressFILE— a fileFORMULA— a formula-backed value
A value outside this list is a 400 invalid_request.
relation_type:optionalstringFor a RELATION property, what it points at. One of ISSUE, USER, RELEASE, or RICH_TEXT. Leave it out — or send null — for every other property type.
description:optionalstringFree-form explanation of what the property captures. Nullable.
is_required:optionalbooleanWhether a value must be supplied for this property.
is_multi:optionalbooleanWhether the property accepts more than one value.
is_active:optionalbooleanWhether the property is in use. Send false to define a property without putting it into circulation yet.
default_value:optionalarray of stringThe value applied when none is supplied. Send an array even for a single value.
options:optionalarray of objectSeed the choices for an OPTION property in the same call. Write-only: the created options come back on the read-only options array of the response, and are managed afterwards through Property options, which is also where the fields an option accepts are documented.
settings:optionalanyType-specific configuration. Its shape depends on property_type.
validation_rules:optionalanyType-specific validation configuration.
external_id:optionalstringYour system's identifier for this property, for sync and import correlation. Maximum 255 characters, nullable.
external_source:optionalstringThe system external_id came from, for example github or jira. Maximum 255 characters, nullable.
Response shaping
fields:optionalstringComma-separated list of fields to return. Unrequested keys are omitted from the response, not returned as null, so absent means "not requested" and null means "actually null". id always comes back whether or not you name it.
Pass all for every requestable field. An unknown name is a 400 that lists the valid set and suggests the closest match, so a typo can't silently cost you the saving.
Requestable here: created_at, default_value, description, display_name, external_id, external_source, id, is_active, is_multi, is_required, logo_props, name, options, property_type, relation_type, settings, validation_rules.
See Sparse fields.
Scopes
workspaces.work_item_properties:write
Errors
| Status | Code | Cause |
|---|---|---|
400 | invalid_request | Missing display_name/property_type, or an enum value outside the allowed set. |
401 | unauthorized | Missing or invalid credentials. |
402 | payment_required | The feature this endpoint belongs to isn't enabled on your plan, or is switched off. |
403 | forbidden | Your role or token scope can't write workspace work item properties. |
404 | not_found | No such workspace, or it's outside your tenant. |
406 | not_acceptable | The Accept header asks for a representation the API can't produce. |
409 | conflict | This workspace manages work item types at the project level. |
413 | payload_too_large | The request body is over the size limit. |
415 | unsupported_media_type | The Content-Type isn't one this endpoint accepts. |
429 | rate_limited | Throttled. Honor the Retry-After header before retrying. |
curl -X POST \
"https://api.plane.so/api/v2/workspaces/my-team/work-item-properties/" \
-H "X-Api-Key: $PLANE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"display_name": "Severity",
"property_type": "OPTION",
"description": "How badly the customer is affected",
"is_required": true,
"is_multi": false,
"options": [
{ "name": "Critical", "description": "Production is down" },
{ "name": "Major", "description": "A core workflow is broken", "is_default": true }
]
}'import requests
response = requests.post(
"https://api.plane.so/api/v2/workspaces/my-team/work-item-properties/",
headers={"X-Api-Key": "your-api-key"},
json={
"display_name": "Severity",
"property_type": "OPTION",
"description": "How badly the customer is affected",
"is_required": True,
"is_multi": False,
"options": [
{"name": "Critical", "description": "Production is down"},
{"name": "Major", "description": "A core workflow is broken", "is_default": True},
],
},
)
print(response.json())const response = await fetch(
"https://api.plane.so/api/v2/workspaces/my-team/work-item-properties/",
{
method: "POST",
headers: {
"X-Api-Key": "your-api-key",
"Content-Type": "application/json",
},
body: JSON.stringify({
display_name: "Severity",
property_type: "OPTION",
description: "How badly the customer is affected",
is_required: true,
is_multi: false,
options: [
{ name: "Critical", description: "Production is down" },
{ name: "Major", description: "A core workflow is broken", is_default: true },
],
}),
},
);
const data = await response.json();{
"id": "a7e3f1d0-5c92-4b68-8f31-2d4a6b9e0c15",
"name": "severity",
"display_name": "Severity",
"description": "How badly the customer is affected",
"property_type": "OPTION",
"relation_type": null,
"is_required": true,
"is_multi": false,
"is_active": true,
"default_value": [],
"options": [
{
"id": "9d2c7b41-6a80-4f35-8e19-5c3b0a7d2e46",
"name": "Critical",
"description": "Production is down",
"is_default": false,
"sort_order": 10000,
"external_id": null,
"external_source": null
},
{
"id": "5a83e0b7-2c46-4d19-9f70-6b12c8e5a03d",
"name": "Major",
"description": "A core workflow is broken",
"is_default": true,
"sort_order": 20000,
"external_id": null,
"external_source": null
}
],
"settings": {},
"validation_rules": {},
"logo_props": {},
"external_id": null,
"external_source": null,
"created_at": "2026-01-14T09:22:41.478363Z"
}{
"type": "invalid_request",
"code": "invalid_request",
"detail": "The request body failed validation.",
"errors": [
{
"field": "property_type",
"code": "invalid_choice",
"message": "\"SELECT\" is not a valid choice."
}
]
}After creating
- Bind it — Create a property context decides which projects and work item types see the property.
- For
OPTIONproperties, refine the choices with Property options. - To put the property on a workspace-level work item type directly, use Attach properties to a workspace type.

