Add states to a workspace workflow
Attach one or more catalog states to a workflow's chain in one call. The request is idempotent. Plane leaves states already in the chain as they are, so sending the same state_ids again adds nothing.
The response is 201 with a JSON array, not a paginated envelope. It holds one chain entry for every id you sent, including ids that were already attached.
New entries get type: "transition", is_default: false, allow_issue_creation: true, and sequence 65535, so they sort after states with a lower sequence. Attaching states doesn't set a default. Use Mark a workspace workflow state as default for that, and send a states body to Update a workspace workflow to set the order.
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.
workflow_id:requiredstring (uuid)The catalog workflow whose chain you are working with.
Body Parameters
state_ids:requiredarray of string (uuid)Catalog state ids to attach. Every id must be a catalog state of this workspace. If any id is a project state, a triage state, or unknown, Plane rejects the whole request with 400 and attaches nothing.
Response shaping
fields:optionalstringComma-separated list of fields to return on each row. 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: allow_issue_creation, id, is_default, sequence, state_id, type, updated_at, workflow_id.
See Sparse fields.
Scopes
workspaces.workflows:write
Errors
| Status | Code | Cause |
|---|---|---|
400 | invalid_request | state_ids is missing or empty, or holds an id that isn't a catalog state of this workspace. |
401 | unauthorized | Missing or invalid credentials. |
402 | payment_required | Workflows isn't on your plan, or is switched off. |
403 | forbidden | Your role or token scope can't edit workspace workflows. |
404 | not_found | No such workflow in this workspace's catalog, or it's outside your tenant. |
406 | not_acceptable | The Accept header asks for a representation the API can't produce. |
409 | governance_migration_in_progress | A governance migration is running in this workspace, so writes are paused. Retry after it finishes. |
409 | states_managed_at_project | The workspace doesn't have governance turned on. Use the project's workflows instead. |
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. |
This error carries an object
Most 400 responses list field errors in an errors array. An invalid state id returns errors as an object with a single error message instead, as in the example.
curl -X POST \
"https://api.plane.so/api/v2/workspaces/my-team/workflows/9c1f0b3d-6d2e-4c9a-8b41-2f7e5a0d6c88/states/" \
-H "X-Api-Key: $PLANE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"state_ids": [
"e5a1c8d3-4b7f-4e29-a6d0-2c9f5b8e1a74"
]
}'import requests
response = requests.post(
"https://api.plane.so/api/v2/workspaces/my-team/workflows/9c1f0b3d-6d2e-4c9a-8b41-2f7e5a0d6c88/states/",
headers={"X-Api-Key": "your-api-key"},
json={
"state_ids": ["e5a1c8d3-4b7f-4e29-a6d0-2c9f5b8e1a74"],
},
)
print(response.json())const response = await fetch(
"https://api.plane.so/api/v2/workspaces/my-team/workflows/9c1f0b3d-6d2e-4c9a-8b41-2f7e5a0d6c88/states/",
{
method: "POST",
headers: {
"X-Api-Key": "your-api-key",
"Content-Type": "application/json",
},
body: JSON.stringify({
state_ids: ["e5a1c8d3-4b7f-4e29-a6d0-2c9f5b8e1a74"],
}),
},
);
const data = await response.json();[
{
"id": "a2d6f9c1-7e3b-4a58-9b14-5c8e0d3f7a62",
"state_id": "e5a1c8d3-4b7f-4e29-a6d0-2c9f5b8e1a74",
"workflow_id": "9c1f0b3d-6d2e-4c9a-8b41-2f7e5a0d6c88",
"type": "transition",
"is_default": false,
"allow_issue_creation": true,
"sequence": 65535.0,
"updated_at": "2026-01-14T09:31:07.215904Z"
}
]{
"type": "invalid_request",
"code": "invalid_request",
"detail": "Invalid state ids.",
"errors": {
"error": "Chain contains invalid or non-catalog state ids"
}
}
