Get a work item type schema
Ask the API what a work item of this type accepts. The response describes the standard fields every work item has — with the project's actual states, priorities, and estimate points inlined as valid options — plus the custom properties attached to this type, including which ones are required and what values they allow.
This is the discovery endpoint. A client that is about to create a work item calls this first, then builds its request body from the answer instead of hardcoding a field list. That matters most for two callers:
- Integrations, which must adapt when an admin adds a required property to a type. Reading the schema each time means a new required field surfaces as a field to fill, not as a
400in production. - AI agents and generated tooling, which need a machine-readable contract.
fieldsandcustom_fieldsare shaped to be handed straight to a function-calling schema: every entry carries a type, a required flag, and its allowed options.
Reads are unaffected by work item type mode — the schema resolves the type's properties whether they are owned by the project or by the workspace.
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.
project_id:requiredstring (uuid)The project the type belongs to. The schema is project-specific: states, labels, members, and estimate points all come from this project.
pk:requiredstring (uuid)The id of the work item type whose schema you want.
Query Parameters
include:optionalstringComma-separated extra option lists to inline. Two values are recognized:
members— inlines the project's active members as the options forassignee_ids, and for any custom property that is a user relationlabels— inlines the project's labels as the options forlabel_ids
Omit it and those fields still appear, they just describe themselves without an option list. These lists can be large in a big workspace, so ask for them only when you are actually rendering a picker — for example ?include=members,labels.
Response Fields
type_id:requiredstring (uuid)The type this schema describes. Send it as type_id on the work item you create. Nullable.
type_name:requiredstringThe type's display name, for example Bug. Nullable.
type_description:requiredstringThe type's description. This is the text a human or an agent reads to decide whether this is the right type for the work at hand, which is why filling it in on create is worth the effort. Nullable.
type_logo_props:requiredanyThe icon and background color rendered next to the type. Nullable.
fields:requiredanyThe standard work item fields, keyed by the body parameter name you would send — name, description_html, priority, state_id, assignee_ids, label_ids, start_date, target_date, parent_id. Each entry carries a type, a required flag, and where it applies an is_multi flag, a default, a max_length, a format, or an options array.
state_id and priority always arrive with their options inlined, because those are the two fields a client cannot guess. estimate_point_id appears only when the project has an estimate system configured.
custom_fields:requiredanyThe custom properties attached to this type, keyed by property name. Each entry carries the property id, its type, name, display_name, description, a required flag, and an is_multi flag.
OPTIONproperties also carry anoptionsarray of{ id, name, logo_props }, narrowed to the options this type allows.RELATIONproperties also carry arelation_type.
Empty when the type has no properties attached, or when custom properties are not available for the workspace.
Scopes
projects.work_item_types:read
Errors
| Status | Code | Cause |
|---|---|---|
401 | unauthorized | Missing or invalid credentials. |
403 | forbidden | Your role or token scope can't read this project's work item types. |
404 | resource_not_found | No such type, project, or workspace — or the type belongs to another project. |
429 | rate_limited | Throttled. Honor the Retry-After header before retrying. |
curl -X GET \
"https://api.plane.so/api/v2/workspaces/my-team/projects/4af68566-94a4-4eb3-94aa-50dc9427067b/work-item-types/d1e7a4c9-2b58-4f36-9a0e-7c3b5d81f240/schema/?include=members,labels" \
-H "X-Api-Key: $PLANE_API_KEY"import requests
response = requests.get(
"https://api.plane.so/api/v2/workspaces/my-team/projects/4af68566-94a4-4eb3-94aa-50dc9427067b/work-item-types/d1e7a4c9-2b58-4f36-9a0e-7c3b5d81f240/schema/",
headers={"X-Api-Key": "your-api-key"},
params={"include": "members,labels"},
)
schema = response.json()
required = [name for name, field in schema["custom_fields"].items() if field["required"]]
print(schema["type_name"], "requires:", required)const params = new URLSearchParams({ include: "members,labels" });
const response = await fetch(
`https://api.plane.so/api/v2/workspaces/my-team/projects/4af68566-94a4-4eb3-94aa-50dc9427067b/work-item-types/d1e7a4c9-2b58-4f36-9a0e-7c3b5d81f240/schema/?${params}`,
{
headers: {
"X-Api-Key": "your-api-key",
},
}
);
const schema = await response.json();
const required = Object.entries(schema.custom_fields)
.filter(([, field]) => field.required)
.map(([name]) => name);{
"type_id": "d1e7a4c9-2b58-4f36-9a0e-7c3b5d81f240",
"type_name": "Bug",
"type_description": "Something is broken and needs a fix",
"type_logo_props": {
"in_use": "icon",
"icon": {
"name": "AlertCircle",
"background_color": "#EF5974"
}
},
"fields": {
"name": {
"type": "string",
"required": true,
"max_length": 255
},
"description_html": {
"type": "string",
"required": false
},
"priority": {
"type": "option",
"required": false,
"default": "none",
"options": [
{ "value": "urgent", "label": "Urgent" },
{ "value": "high", "label": "High" },
{ "value": "medium", "label": "Medium" },
{ "value": "low", "label": "Low" },
{ "value": "none", "label": "None" }
]
},
"state_id": {
"type": "uuid",
"required": false,
"options": [
{
"id": "2c4d16f8-9b3e-4a52-8d71-1f0e6c9a5b48",
"name": "Backlog",
"color": "#8b8d98",
"group": "backlog"
},
{
"id": "f960d3c2-8524-4a41-b8eb-055ce4be2a7f",
"name": "In Progress",
"color": "#3f76ff",
"group": "started"
},
{
"id": "7b1e9d40-3c86-4f2a-9a5d-8e2b0c47d613",
"name": "Done",
"color": "#26a05f",
"group": "completed"
}
]
},
"assignee_ids": {
"type": "uuid",
"is_multi": true,
"required": false,
"options": [
{
"id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
"display_name": "rin",
"email": "rin@my-team.dev"
}
]
},
"label_ids": {
"type": "uuid",
"is_multi": true,
"required": false,
"options": [
{
"id": "5e0c7b93-1a24-4d68-8f31-9b7e2c05a4d7",
"name": "regression",
"color": "#f5a623"
}
]
},
"start_date": {
"type": "date",
"required": false,
"format": "YYYY-MM-DD"
},
"target_date": {
"type": "date",
"required": false,
"format": "YYYY-MM-DD"
},
"parent_id": {
"type": "uuid",
"required": false
}
},
"custom_fields": {
"severity": {
"id": "a8b31d67-5e42-4c09-9f78-2d6b41e0c395",
"type": "OPTION",
"name": "severity",
"display_name": "Severity",
"description": "How badly this breaks the product",
"required": true,
"is_multi": false,
"options": [
{
"id": "3f8c2a71-6d90-4e15-b243-0c9e7a5b16f8",
"name": "S1",
"logo_props": {}
},
{
"id": "b47e0d29-8153-4a6c-9e70-2f1d5c83a904",
"name": "S2",
"logo_props": {}
}
]
},
"found_by": {
"id": "c0a95f38-7b1e-4d62-8305-6e4a9b27fd51",
"type": "RELATION",
"name": "found_by",
"display_name": "Found by",
"description": "",
"required": false,
"is_multi": false,
"relation_type": "USER",
"options": [
{
"id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
"display_name": "rin",
"email": "rin@my-team.dev"
}
]
}
}
}How to use the schema
The response is a plan for the next request. A client that reads it correctly needs no hardcoded knowledge of the project's configuration.
- Pick the type. Read
type_nameandtype_descriptionto confirm this is the right type, and carrytype_idinto the work item body. - Fill the standard fields from
fields. Anything with"required": truemust be present. Where an entry hasoptions, send one of thoseidvalues — a state id from another project is a400, never a silent link. - Fill the custom properties from
custom_fields. Send them undercustom_fieldson the work item, keyed by the same property name. Entries with"required": truemust be present on create. - Respect
is_multi. A field with"is_multi": truetakes an array; one without takes a single value.
Re-read the schema, don't cache it forever
Admins add and retire properties. A property that became required since your last sync is the most common cause of a surprise 400 on work item create. Reading the schema at the start of a sync run costs one request and removes the failure mode entirely.
Property types
custom_fields entries carry the property's type. The possible values are TEXT, DATETIME, DECIMAL, BOOLEAN, OPTION, RELATION, URL, EMAIL, FILE, and FORMULA.
A RELATION property additionally carries relation_type, one of ISSUE, USER, RELEASE, or RICH_TEXT, telling you what kind of id its value refers to.
Options are narrowed per type
The options list on an OPTION property is what this type allows, which can be a subset of every option defined on the property. Read the options from the schema rather than from the property options endpoint when you want the values valid for this specific type.
Related
- Attach a property to a type — what puts an entry into
custom_fields - Create a work item property — defining the property in the first place
- Create a work item — where the schema gets spent

