Update a label
Change a label's name, color, description, nesting, or ordering. Every work item already carrying the label picks up the change — the label id does not move, so no work item loses its tag when you rename it.
The update is partial. Fields you omit are left untouched, and omitting a field is not the same as sending null: send "parent_id": null to lift a nested label to the top level.
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 label belongs to.
pk:requiredstring (uuid)The label id.
Body Parameters
name:optionalstringNew display name, unique within the project. Maximum 255 characters. Renaming onto a name another label already holds returns 409 conflict.
color:optionalstringHex color used wherever the label is rendered, for example #e5484d. Maximum 255 characters.
description:optionalstringFree-form note about what the label is for.
parent_id:optionalstring (uuid)Move the label under a different parent. The new parent must be a label in the same project. Send null to make the label top-level again.
sort_order:optionalnumberOrdering weight within the project. Lower values sort first.
external_id:optionalstringYour system's identifier for this label, for sync and import correlation. Maximum 255 characters. Send null to clear it.
external_source:optionalstringThe system external_id came from, for example github or jira. Maximum 255 characters. Send null to clear it.
Scopes
projects.labels:write
Errors
| Status | Code | Cause |
|---|---|---|
400 | validation_error | A field over its length limit, or a parent_id that isn't a label in this project. |
401 | unauthorized | Missing or invalid credentials. |
403 | forbidden | Your role or token scope can't update labels. |
404 | resource_not_found | No such label in this project, or the workspace or project is outside your tenant. |
409 | conflict | Another label in the project already uses the name you sent. |
429 | rate_limited | Throttled. Honor the Retry-After header before retrying. |
There is no PUT
PUT on this path returns 405 method_not_allowed. Send only the fields you want to change with PATCH — a full-object write is never required, and rebuilding the object client-side risks clobbering a field someone else just changed.
curl -X PATCH \
"https://api.plane.so/api/v2/workspaces/my-team/projects/4af68566-94a4-4eb3-94aa-50dc9427067b/labels/9c1f0b3d-6d2e-4c9a-8b41-2f7e5a0d6c88/" \
-H "X-Api-Key: $PLANE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Regression - blocking",
"color": "#c62828"
}'import requests
response = requests.patch(
"https://api.plane.so/api/v2/workspaces/my-team/projects/4af68566-94a4-4eb3-94aa-50dc9427067b/labels/9c1f0b3d-6d2e-4c9a-8b41-2f7e5a0d6c88/",
headers={"X-Api-Key": "your-api-key"},
json={
"name": "Regression - blocking",
"color": "#c62828",
},
)
print(response.json())const response = await fetch(
"https://api.plane.so/api/v2/workspaces/my-team/projects/4af68566-94a4-4eb3-94aa-50dc9427067b/labels/9c1f0b3d-6d2e-4c9a-8b41-2f7e5a0d6c88/",
{
method: "PATCH",
headers: {
"X-Api-Key": "your-api-key",
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Regression - blocking",
color: "#c62828",
}),
}
);
const data = await response.json();{
"id": "9c1f0b3d-6d2e-4c9a-8b41-2f7e5a0d6c88",
"name": "Regression - blocking",
"description": "Worked before the last release",
"color": "#c62828",
"sort_order": 65535,
"parent_id": "2b7d5e94-3c1a-4f60-9a8d-7e1c4b0f2d35",
"external_id": null,
"external_source": null,
"created_at": "2026-01-14T09:22:41.478363Z",
"created_by_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430"
}{
"type": "https://api.plane.so/errors/conflict",
"title": "Conflict",
"status": 409,
"code": "conflict",
"detail": "A label with this name already exists in the project."
}
