Create a workspace state
Add a state to the workspace catalog. Every project in the workspace can use the new state.
This works only in a workspace with governance turned on. In an ungoverned workspace, states belong to projects, and this endpoint returns 409 states_managed_at_project. Create the state with Create a state on the project instead.
If the workspace's plan doesn't include workflows, Plane also adds the new state to the workspace's default workflow, after the existing states of the same group. If the plan includes workflows, add the state to a workflow yourself with Workspace workflow states.
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
name:requiredstringDisplay name. Maximum 255 characters. It must be unique in the catalog, compared case-insensitively, and a clash returns 409 conflict. Triage is reserved regardless of case or surrounding spaces, and sending it returns 400 invalid_request.
color:requiredstringHex color used wherever the state is rendered, for example #3f76ff. Maximum 255 characters.
group:requiredstringThe workflow group the state belongs to.
backlog. Not yet scheduled.unstarted. Scheduled but not begun.started. Actively in progress.completed. Finished successfully.cancelled. Closed without completion.
triage is not accepted, because Plane manages the triage state itself.
description:optionalstringFree-form description of what the state means.
sequence:optionalnumberOrdering weight. Lower values sort first. Plane places a new state after the last catalog state and uses your value only when the catalog is empty. To reposition a state, send sequence in Update a workspace state.
external_id:optionalstringYour system's identifier for this state, for sync and import correlation. Maximum 255 characters.
external_source:optionalstringThe system external_id came from, for example jira. Maximum 255 characters. When you send both external_id and external_source, the pair must be unused in the catalog. A clash returns 409 conflict with the existing 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 | Missing name, color, or group, a group outside the five catalog groups, or the name Triage. |
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 create catalog states, or your token lacks the scope. |
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 | states_managed_at_project | The workspace doesn't have governance turned on. Create the state on a project. |
409 | conflict | A 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. |
curl -X POST \
"https://api.plane.so/api/v2/workspaces/my-team/states/" \
-H "X-Api-Key: $PLANE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "In Review",
"color": "#8e4ec6",
"group": "started",
"description": "Awaiting code review"
}'import requests
response = requests.post(
"https://api.plane.so/api/v2/workspaces/my-team/states/",
headers={"X-Api-Key": "your-api-key"},
json={
"name": "In Review",
"color": "#8e4ec6",
"group": "started",
"description": "Awaiting code review",
},
)
print(response.json())const response = await fetch("https://api.plane.so/api/v2/workspaces/my-team/states/", {
method: "POST",
headers: {
"X-Api-Key": "your-api-key",
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "In Review",
color: "#8e4ec6",
group: "started",
description: "Awaiting code review",
}),
});
const data = await response.json();{
"id": "9e1b7c35-2a4f-4d08-b6e3-1c5f8a2d7e90",
"name": "In Review",
"description": "Awaiting code review",
"color": "#8e4ec6",
"group": "started",
"sequence": 60000,
"is_default": false,
"is_triage": false,
"project_id": null,
"external_id": null,
"external_source": null,
"created_at": "2026-02-03T16:45:12.904117Z",
"updated_at": "2026-02-03T16:45:12.904117Z",
"created_by_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
"updated_by_id": null
}{
"type": "conflict",
"code": "states_managed_at_project",
"detail": "States are managed at the project level for this workspace."
}{
"type": "conflict",
"code": "conflict",
"detail": "A state with this name already exists in this workspace."
}{
"type": "invalid_request",
"code": "invalid_request",
"detail": "One or more fields failed validation.",
"errors": [
{
"field": "group",
"code": "invalid_choice",
"message": "\"triage\" is not a valid choice."
}
]
}
