Skip to content

Create a state ​

POST/api/v2/workspaces/{slug}/projects/{project_id}/states/

Add a workflow state to a project. The new state is appended to the project's workflow and becomes immediately selectable on work items.

State names must be unique within a project — reusing one returns 409 conflict.

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 add the state to.

Body Parameters ​

name:requiredstring

Display name for the state, unique within the project. Maximum 255 characters.

color:requiredstring

Hex color used wherever the state is rendered, for example #3f76ff. Maximum 255 characters.

group:optionalstring

The workflow group the state belongs to. Determines how the state is treated by boards, charts, and cycle progress.

  • backlog — Not yet scheduled
  • unstarted — Scheduled but not begun
  • started — Actively in progress
  • completed — Finished successfully
  • cancelled — Closed without completion
  • triage — Awaiting intake review

Defaults to backlog when omitted.

description:optionalstring

Free-form description of what the state means in this workflow.

sequence:optionalnumber

Ordering weight within the project. Lower values sort first. Assigned automatically when omitted.

is_default:optionalboolean

Make this the project's default state — where work items land when no state_id is supplied. Setting it clears the flag on the previous default.

external_id:optionalstring

Your system's identifier for this state, for sync and import correlation. Maximum 255 characters.

external_source:optionalstring

The system external_id came from, for example github or jira. Maximum 255 characters.

Response shaping ​

fields:optionalstring

Comma-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, sequence.

See Sparse fields.

Scopes ​

projects.states:write

Errors ​

StatusCodeCause
400invalid_requestMissing name/color, or a group outside the enum.
401unauthorizedMissing or invalid credentials.
402payment_requiredThe feature this endpoint belongs to isn't enabled on your plan, or is switched off.
403forbiddenYour role or token scope can't create states.
404not_foundNo such workspace or project, or it's outside your tenant.
406not_acceptableThe Accept header asks for a representation the API can't produce.
409conflictA state with this name already exists in the project.
413payload_too_largeThe request body is over the size limit.
415unsupported_media_typeThe Content-Type isn't one this endpoint accepts.
429rate_limitedThrottled. Honor the Retry-After header before retrying.
Create a state
bash
curl -X POST \
  "https://api.plane.so/api/v2/workspaces/my-team/projects/4af68566-94a4-4eb3-94aa-50dc9427067b/states/" \
  -H "X-Api-Key: $PLANE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "In Review",
  "color": "#3f76ff",
  "group": "started",
  "description": "Awaiting code review"
}'
Response201
json
{
  "id": "b7e42a19-3c5d-4f80-9a26-8d1c0f4e7b53",
  "name": "In Review",
  "description": "Awaiting code review",
  "color": "#3f76ff",
  "group": "started",
  "sequence": 45000,
  "is_default": false,
  "is_triage": false,
  "external_id": null,
  "external_source": null,
  "created_at": "2026-01-14T09:22:41.478363Z",
  "created_by_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430"
}
Response409
json
{
  "type": "conflict",
  "code": "conflict",
  "detail": "A state with this name already exists in this project."
}