Create a project label
Add a label to the workspace's project label catalog. Only workspace owners and admins can create project labels.
Names are unique in the workspace without regard to case. If the name is taken, the 409 conflict body carries the existing label's id, so an importer can use that label instead.
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. A name already in use, in any letter case, returns 409 conflict.
color:optionalstringHex color used wherever the label is rendered, for example #e5484d. Maximum 255 characters. Can be empty.
description:optionalstringFree-form note about what the label is for. Can be empty.
sort_order:optionalnumberOrdering weight. Lower values sort first. Plane places a new label after the last one and uses your value only when the workspace has no project labels. To reposition a label, send sort_order in Update a project label.
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, id, name, sort_order, updated_at, updated_by_id.
See Sparse fields.
Scopes
workspaces.project_labels:write
Errors
| Status | Code | Cause |
|---|---|---|
400 | invalid_request | Missing name, 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 create project labels, 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 | conflict | A project label with this name, in any letter case, already exists. The body carries its id. |
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/project-labels/" \
-H "X-Api-Key: $PLANE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Customer-facing",
"color": "#e5484d",
"description": "Projects that ship changes customers see"
}'import requests
response = requests.post(
"https://api.plane.so/api/v2/workspaces/my-team/project-labels/",
headers={"X-Api-Key": "your-api-key"},
json={
"name": "Customer-facing",
"color": "#e5484d",
"description": "Projects that ship changes customers see",
},
)
print(response.json())const response = await fetch("https://api.plane.so/api/v2/workspaces/my-team/project-labels/", {
method: "POST",
headers: {
"X-Api-Key": "your-api-key",
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Customer-facing",
color: "#e5484d",
description: "Projects that ship changes customers see",
}),
});
const data = await response.json();{
"id": "71c3e8a9-4d2f-4b60-9e17-5a8c0b3f2d94",
"name": "Customer-facing",
"description": "Projects that ship changes customers see",
"color": "#e5484d",
"sort_order": 85535,
"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": "conflict",
"detail": "A project label with this name already exists in this workspace.",
"id": "d8a4f2c6-1e7b-4093-9c5a-6b2e0f8d3a71"
}
