Skip to content

Create a work item property

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

Define a new custom field in a project. You choose a label and a property_type, and Plane returns the property definition — including the derived name slug you can key off in your own storage.

Two things to know before you send this request:

  • Creating a property does not put it on any work item. The definition exists, but nothing renders it until you attach it to a work item type with Attach a type property.
  • You write display_name, not name. name is the read-side slug Plane derives; it is not a body field.

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 to define the property in. Project-level properties belong to this project alone.

Body Parameters

display_name:requiredstring

The human-readable label for the field, for example Severity. Maximum 255 characters. Plane derives the read-only name slug from this value and returns both.

property_type:requiredstring

What kind of data the field holds. This is the decision that shapes everything else about the property.

  • TEXT — Free-form text
  • DATETIME — A point in time, written as an ISO 8601 timestamp
  • DECIMAL — A number, carried as a string in the value array
  • BOOLEAN — A yes/no flag
  • OPTION — A choice from a fixed set you define
  • RELATION — A reference to another record; pair it with relation_type
  • URL — A link
  • EMAIL — An email address
  • FILE — An uploaded file
  • FORMULA — A value Plane computes rather than one a person enters

The full reference, including what each type means for the value you send on a work item, is on the properties overview.

relation_type:optionalstring

What a RELATION property points at. Only meaningful when property_type is RELATION — leave it out otherwise.

  • ISSUE — A work item
  • USER — A member
  • RELEASE — A release
  • RICH_TEXT — Rich text content
description:optionalstring

Free-form explanation of what the field is for. Worth filling in — it is the helper text people read when deciding what to type.

options:optionalarray of object

Write-only. Define the choices for an OPTION property inline, in the same request that creates it, instead of a second round-trip per choice.

Each entry is a property option object — name is required, and description, is_default, external_id, and external_source are accepted. See Create a property option for the full field list.

You never get options back in the shape you sent it. The response carries the resolved options array, with each choice's generated id and sort_order.

is_multi:optionalboolean

Allow more than one value on a work item — a multi-select list of severities, a set of reviewers, several linked work items. Leave it off for a single-valued field.

is_required:optionalboolean

Force a value to be present. Give a required property a default_value so existing flows have something to fall back on.

is_active:optionalboolean

Whether the property is offered. Create it inactive if you want to define the field and its options now but roll it out later.

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. A property with no default sends [] or omits the field.

For an OPTION property, mark the default choice with is_default on the option itself rather than repeating it here.

settings:optionalany

Free-form object holding type-specific configuration. What belongs in it depends entirely on property_type, so there is no single schema — send the whole object rather than assuming keys.

validation_rules:optionalany

Free-form object holding type-specific validation constraints. Same shape 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_errorMissing display_name or property_type, or an enum value outside the list.
401unauthorizedMissing or invalid credentials.
403forbiddenYour role or token scope can't create properties.
404resource_not_foundNo such workspace or project, or it's outside your tenant.
409work_item_types_managed_at_workspaceThis workspace manages work item types at the workspace level. Create 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. Nothing is broken and no permission is missing — the same property is created through Create a workspace work item property. See Work item type modes for how to detect which mode you are in.

Create a work item property
bash
curl -X POST \
  "https://api.plane.so/api/v2/workspaces/my-team/projects/4af68566-94a4-4eb3-94aa-50dc9427067b/work-item-properties/" \
  -H "X-Api-Key: $PLANE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "display_name": "Severity",
  "description": "How badly this affects customers",
  "property_type": "OPTION",
  "is_multi": false,
  "is_required": true,
  "is_active": true,
  "options": [
    { "name": "Critical", "description": "Production is down" },
    { "name": "Major", "description": "A core workflow is broken", "is_default": true },
    { "name": "Minor", "description": "Cosmetic or low impact" }
  ]
}'
Response201
json
{
  "id": "9d2f0b74-6a51-4c8e-b3d7-2f1a8c05e964",
  "name": "severity",
  "display_name": "Severity",
  "description": "How badly this affects customers",
  "property_type": "OPTION",
  "relation_type": null,
  "is_multi": false,
  "is_required": true,
  "is_active": true,
  "default_value": [],
  "options": [
    {
      "id": "3e7a5c19-42b8-4d06-9f3e-7c1b8a0d2456",
      "name": "Critical",
      "description": "Production is down",
      "is_default": false,
      "sort_order": 15000,
      "external_id": null,
      "external_source": null
    },
    {
      "id": "b6c04f83-1d29-4e57-8a3b-90e2f5c7d418",
      "name": "Major",
      "description": "A core workflow is broken",
      "is_default": true,
      "sort_order": 25000,
      "external_id": null,
      "external_source": null
    },
    {
      "id": "7f52d3a8-0e14-4c69-b28d-a1f6e903c5b7",
      "name": "Minor",
      "description": "Cosmetic or low impact",
      "is_default": false,
      "sort_order": 35000,
      "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"
}
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."
}
Response400
json
{
  "type": "https://api.plane.so/errors/validation-error",
  "title": "Validation Error",
  "status": 400,
  "code": "validation_error",
  "detail": "The request body failed validation.",
  "errors": [
    {
      "field": "property_type",
      "message": "\"SELECT\" is not a valid choice."
    }
  ]
}

Creating a relation property

A RELATION property needs relation_type to say what it points at. This one lets a work item name several reviewers:

json
{
  "display_name": "Reviewer",
  "description": "Who signs this off",
  "property_type": "RELATION",
  "relation_type": "USER",
  "is_multi": true
}

relation_type stays null on every non-RELATION property.

After you create

  1. Attach it to a type. Attach a type property is what makes the field appear on work items. One property can be attached to several types.
  2. Add or adjust options later. Inline options covers the initial set; Property options handles changes over the property's life.