Update a project template
Change a project template's name, description, publication flag, or project seed. Projects already created from the template don't change.
The update is partial for the top-level fields. Send only the ones you want to change, and anything you omit keeps its current value. A request without template_data leaves the seed and its work items untouched.
template_data, which carries the bundled work items, has its own rules:
workitemsis replaced as a whole. The list you send becomes the template's work items, and the old ones are deleted. If you sendtemplate_datawithout aworkitemskey, the template ends up with no work items.- When you omit them,
membersresets to[]andcover_assetresets to an empty string. - Every other key merges. A key you send overwrites the stored value, and a key you omit keeps it.
template_data.nameis required, even when you aren't changing it.
To change part of the seed, get the template, edit its template_data, and send it back whole.
Path Parameters
slug:requiredstringThe workspace slug. It appears in your Plane URLs, so in https://app.plane.so/my-team/projects/ the slug is my-team.
pk:requiredstring (uuid)The id of the project template to update.
Body Parameters
Every field is optional.
name:optionalstringNew display name. Maximum 255 characters. Changing it also regenerates slug.
short_description:optionalstringOne-line summary of what the template is for.
description_html:optionalstringLonger description of the template itself, as HTML.
is_published:optionalbooleanWhether the template is published. Other workspaces can copy a template only while it is published.
template_data:optionalobjectChanges to the project seed, with the same keys and shape checks as on create. name is required. workitems, members, and cover_asset follow the replace and reset rules above, and every other key merges into the stored seed.
See The template_data object for every key.
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: category_ids, created_at, created_by_id, description_html, id, is_published, is_verified, name, short_description, short_id, slug, template_data, template_type, updated_at, updated_by_id.
See Sparse fields.
Scopes
templates.projects:write
Errors
| Status | Code | Cause |
|---|---|---|
400 | invalid_request | template_data is missing name, a value is outside its allowed set or range, or a key or work item seed fails the shape check. |
401 | unauthorized | Missing or invalid credentials. |
402 | payment_required | Project templates aren't in your plan, or the feature is switched off for this workspace. |
403 | forbidden | Your role or token scope can't edit project templates. |
404 | not_found | No project template with this id in this workspace. |
406 | not_acceptable | The Accept header asks for a representation the API can't produce. |
409 | governance_migration_in_progress | The workspace is migrating to workspace governance. Writes are blocked until it finishes, so retry later. |
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. |
Sending template_data without workitems deletes the work items
{"template_data": {"name": "Service project", "priority": "urgent"}} changes the priority and also removes every bundled work item. Include the full workitems list you read from the template whenever you send template_data.
curl -X PATCH \
"https://api.plane.so/api/v2/workspaces/my-team/project-templates/5b7e3c1a-9d24-4f68-b5a0-2e8c6d4f1b97/" \
-H "X-Api-Key: $PLANE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"is_published": true,
"template_data": {
"name": "Service project",
"priority": "urgent",
"workitems": [
{
"name": "Set up CI",
"description_html": "<p>Build, test, and deploy on every merge.</p>",
"priority": "high",
"state": { "id": "a1f4c7e2-5b8d-4e19-9c63-2f7a0d4b8e15", "name": "Backlog", "group": "backlog" },
"labels": [{ "id": "d4b7a2e9-1f63-4c85-9e02-6b3d8f1a7c54", "name": "infra", "color": "#3f76ff" }]
},
{
"name": "Write the runbook",
"priority": "low",
"state": { "id": "a1f4c7e2-5b8d-4e19-9c63-2f7a0d4b8e15", "name": "Backlog", "group": "backlog" }
},
{
"name": "Add dashboards and alerts",
"priority": "medium",
"state": { "id": "a1f4c7e2-5b8d-4e19-9c63-2f7a0d4b8e15", "name": "Backlog", "group": "backlog" }
}
]
}
}'import requests
url = "https://api.plane.so/api/v2/workspaces/my-team/project-templates/5b7e3c1a-9d24-4f68-b5a0-2e8c6d4f1b97/"
headers = {"X-Api-Key": "your-api-key"}
# Read the current seed so the PATCH keeps the existing work items.
template_data = requests.get(url, headers=headers).json()["template_data"]
template_data["priority"] = "urgent"
template_data["workitems"].append(
{
"name": "Add dashboards and alerts",
"priority": "medium",
"state": {"id": "a1f4c7e2-5b8d-4e19-9c63-2f7a0d4b8e15", "name": "Backlog", "group": "backlog"},
}
)
response = requests.patch(
url,
headers=headers,
json={"is_published": True, "template_data": template_data},
)
print(response.json())const url =
"https://api.plane.so/api/v2/workspaces/my-team/project-templates/5b7e3c1a-9d24-4f68-b5a0-2e8c6d4f1b97/";
const headers = { "X-Api-Key": "your-api-key", "Content-Type": "application/json" };
// Read the current seed so the PATCH keeps the existing work items.
const { template_data } = await (await fetch(url, { headers })).json();
template_data.priority = "urgent";
template_data.workitems.push({
name: "Add dashboards and alerts",
priority: "medium",
state: { id: "a1f4c7e2-5b8d-4e19-9c63-2f7a0d4b8e15", name: "Backlog", group: "backlog" },
});
const response = await fetch(url, {
method: "PATCH",
headers,
body: JSON.stringify({ is_published: true, template_data }),
});
const data = await response.json();{
"id": "5b7e3c1a-9d24-4f68-b5a0-2e8c6d4f1b97",
"name": "Service launch",
"short_description": "States, labels, and starter work items for a new service",
"description_html": "<p>Use this for every new backend service.</p>",
"template_type": "project",
"is_published": true,
"is_verified": false,
"short_id": "p7m2qa",
"slug": "service-launch",
"category_ids": [],
"template_data": {
"id": "e4a9c2f7-1b38-4d5e-9f60-7a2c8b3d1e45",
"name": "Service project",
"description": "Bootstraps a new backend service",
"network": 2,
"default_assignee": {},
"project_lead": {},
"logo_props": {},
"cover_asset": "",
"module_view": true,
"cycle_view": true,
"issue_views_view": true,
"page_view": true,
"intake_view": false,
"is_time_tracking_enabled": false,
"is_issue_type_enabled": false,
"is_project_updates_enabled": false,
"is_epic_enabled": false,
"is_workflow_enabled": false,
"is_milestone_enabled": false,
"guest_view_all_features": false,
"timezone": "UTC",
"archive_in": 0,
"close_in": 0,
"states": [
{
"id": "a1f4c7e2-5b8d-4e19-9c63-2f7a0d4b8e15",
"name": "Backlog",
"color": "#a3a3a3",
"group": "backlog",
"default": true,
"sequence": 15000
},
{
"id": "c8e3f1b5-7a24-4d96-b0e1-4a6d8c2f9b37",
"name": "Done",
"color": "#16a34a",
"group": "completed",
"default": false,
"sequence": 25000
}
],
"priority": "urgent",
"project_state": {},
"start_date": null,
"target_date": null,
"labels": [
{
"id": "d4b7a2e9-1f63-4c85-9e02-6b3d8f1a7c54",
"name": "infra",
"color": "#3f76ff"
}
],
"workflows": {},
"estimates": {},
"workitem_types": {},
"epics": {},
"members": [],
"intake_settings": {},
"milestones": {},
"modules": {},
"recurring_work_items": {},
"automations": {},
"workitems": [
{
"name": "Set up CI",
"description_html": "<p>Build, test, and deploy on every merge.</p>",
"priority": "high",
"state": {
"id": "a1f4c7e2-5b8d-4e19-9c63-2f7a0d4b8e15",
"name": "Backlog",
"group": "backlog"
},
"assignees": [],
"labels": [
{
"id": "d4b7a2e9-1f63-4c85-9e02-6b3d8f1a7c54",
"name": "infra",
"color": "#3f76ff"
}
],
"type": {},
"modules": [],
"properties": []
},
{
"name": "Write the runbook",
"description_html": "<p></p>",
"priority": "low",
"state": {
"id": "a1f4c7e2-5b8d-4e19-9c63-2f7a0d4b8e15",
"name": "Backlog",
"group": "backlog"
},
"assignees": [],
"labels": [],
"type": {},
"modules": [],
"properties": []
},
{
"name": "Add dashboards and alerts",
"description_html": "<p></p>",
"priority": "medium",
"state": {
"id": "a1f4c7e2-5b8d-4e19-9c63-2f7a0d4b8e15",
"name": "Backlog",
"group": "backlog"
},
"assignees": [],
"labels": [],
"type": {},
"modules": [],
"properties": []
}
]
},
"created_at": "2026-01-14T09:22:41.478363Z",
"updated_at": "2026-02-03T16:45:12.904117Z",
"created_by_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
"updated_by_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430"
}
