Update a workspace state
Change a catalog state's name, color, description, group, position, or external ids. Work items in the state stay where they are. The change applies in every project that uses the state.
PATCH is partial. Send only the fields you want to change. Anything you omit keeps its current value.
This endpoint addresses only catalog states, so it works only in a workspace with governance turned on. In an ungoverned workspace, a project's state id returns 404. Update it with Update a state on its project.
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.
pk:requiredstring (uuid)The id of the catalog state to update. The triage state's id returns 404.
Body Parameters
Every field is optional. Send the subset you are changing.
name:optionalstringNew display name. Maximum 255 characters. It must be unique in the catalog, compared case-insensitively, and a clash returns 409 conflict. Changing only the case of the state's own name is allowed. Triage is reserved and returns 400 invalid_request.
color:optionalstringHex color used wherever the state is rendered, for example #3f76ff. Maximum 255 characters.
description:optionalstringFree-form description of what the state means.
group:optionalstringMove the state to a different workflow group. One of backlog, unstarted, started, completed, or cancelled. triage is not accepted.
The group decides how boards, charts, and cycle progress count every work item in the state, in every project. Moving a state from started to completed makes those work items count as completed.
sequence:optionalnumberOrdering weight. Lower values sort first. Set it to reposition the state in the catalog.
external_id:optionalstringYour system's identifier for this state. Maximum 255 characters.
external_source:optionalstringThe system external_id came from. Maximum 255 characters. The resulting external_id and external_source pair must not belong to another catalog state. A clash returns 409 conflict with that state's id in the body.
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: color, created_at, created_by_id, description, external_id, external_source, group, id, is_default, is_triage, name, project_id, sequence, updated_at, updated_by_id.
See Sparse fields.
Scopes
workspaces.states:write
Errors
| Status | Code | Cause |
|---|---|---|
400 | invalid_request | A group outside the five catalog groups, the name Triage, or a field over its length limit. |
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 | Only workspace owners and admins can update catalog states, or your token lacks the scope. |
404 | not_found | No catalog state with this id, or it's the triage state. |
406 | not_acceptable | The Accept header asks for a representation the API can't produce. |
409 | states_managed_at_project | The state is a catalog state, but the workspace doesn't have governance turned on. |
409 | conflict | Another catalog state already uses this name, or this external_id and external_source pair. |
409 | governance_migration_in_progress | A governance migration is running, so the workspace is read-only. Retry after it finishes. |
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. |
The 404 here has no body
When no catalog state matches pk, this endpoint returns 404 with an empty body. Check the status code, not the code field.
curl -X PATCH \
"https://api.plane.so/api/v2/workspaces/my-team/states/5a9e2c71-1f3b-4d86-a0c4-7e2b9d5f8a13/" \
-H "X-Api-Key: $PLANE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Building",
"color": "#f5a623"
}'import requests
response = requests.patch(
"https://api.plane.so/api/v2/workspaces/my-team/states/5a9e2c71-1f3b-4d86-a0c4-7e2b9d5f8a13/",
headers={"X-Api-Key": "your-api-key"},
json={
"name": "Building",
"color": "#f5a623",
},
)
print(response.json())const response = await fetch(
"https://api.plane.so/api/v2/workspaces/my-team/states/5a9e2c71-1f3b-4d86-a0c4-7e2b9d5f8a13/",
{
method: "PATCH",
headers: {
"X-Api-Key": "your-api-key",
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Building",
color: "#f5a623",
}),
},
);
const data = await response.json();{
"id": "5a9e2c71-1f3b-4d86-a0c4-7e2b9d5f8a13",
"name": "Building",
"description": "Actively being worked on",
"color": "#f5a623",
"group": "started",
"sequence": 30000,
"is_default": false,
"is_triage": false,
"project_id": null,
"external_id": null,
"external_source": null,
"created_at": "2026-01-14T09:22:41.478363Z",
"updated_at": "2026-02-03T16:45:12.904117Z",
"created_by_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
"updated_by_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430"
}{
"type": "conflict",
"code": "conflict",
"detail": "A state with this name already exists in this workspace."
}
