Delete a state
Remove a state from a project's workflow. A successful delete returns 204 with an empty body.
Two conditions block a delete, and you have to clear the one that applies before the delete succeeds. See Before you delete.
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 state belongs to.
pk:requiredstring (uuid)The id of the state to delete.
Response shaping
fields:optionalstringComma-separated list of fields to return. Fields you don't request are left out of the response instead of coming back as null, so a missing key means you didn't ask for it and null means the value is null. id is always included, whether or not you name it.
Pass all to get every requestable field. An unknown field name returns 400 with the list of valid names and the closest match, so a typo is caught instead of being silently ignored.
Requestable here: color, created_at, created_by_id, description, external_id, external_source, group, id, is_default, is_triage, name, sequence, updated_at, updated_by_id.
See Sparse fields.
Scopes
projects.states:write
Errors
| Status | Code | Cause |
|---|---|---|
400 | invalid_request | The request body or a query parameter failed validation. |
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 | Your role or token scope can't delete states. |
404 | not_found | No such state, project, or workspace, or it's outside your tenant. |
406 | not_acceptable | The Accept header asks for a representation the API can't produce. |
409 | conflict | The state is the project's default state. |
409 | conflict | The state still holds work items. |
409 | states_managed_at_workspace | The workspace has governance turned on, so project states are read-only. See Workspace governance. |
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 DELETE \
"https://api.plane.so/api/v2/workspaces/my-team/projects/4af68566-94a4-4eb3-94aa-50dc9427067b/states/f960d3c2-8524-4a41-b8eb-055ce4be2a7f/" \
-H "X-Api-Key: $PLANE_API_KEY"import requests
response = requests.delete(
"https://api.plane.so/api/v2/workspaces/my-team/projects/4af68566-94a4-4eb3-94aa-50dc9427067b/states/f960d3c2-8524-4a41-b8eb-055ce4be2a7f/",
headers={"X-Api-Key": "your-api-key"},
)
print(response.status_code)const response = await fetch(
"https://api.plane.so/api/v2/workspaces/my-team/projects/4af68566-94a4-4eb3-94aa-50dc9427067b/states/f960d3c2-8524-4a41-b8eb-055ce4be2a7f/",
{
method: "DELETE",
headers: {
"X-Api-Key": "your-api-key",
},
},
);
console.log(response.status);No response body.
{
"type": "conflict",
"code": "conflict",
"detail": "The default state cannot be deleted."
}{
"type": "conflict",
"code": "conflict",
"detail": "Only an empty state (no work items) can be deleted."
}Before you delete
Both protected cases return 409 conflict. Use the detail only for messaging, because the fix differs:
- If the state is the project's default: every project needs somewhere for work items to land when no
state_idis supplied. Promote another state with Update a state and"is_default": true, which demotes the current default, then delete it. - If the state still holds work items: deleting it would leave those work items without a status. Move them to another state first. Filter the project's work items by this state,
PATCHeach one to the replacement state, then retry the delete.
So the safe order is: reassign the work items, move the default flag if this state has it, then delete the state.
Deletes are soft
The state stops appearing in the API and in Plane, but the row is retained. Treat the 204 as final for integration purposes, because the states API has no restore operation.

