Create a workspace page template
Create a page template that belongs to the whole workspace. The request has two parts. The top-level fields describe the template in the picker, and template_data is the page that the template creates.
The template stays workspace-level for its whole life. To create one that belongs to a project, use Create a project page template.
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.
Body Parameters
name:requiredstringDisplay name of the template, shown in the template picker. Maximum 255 characters.
template_data:requiredobjectThe page seed. Keys:
name(string, required). Title of the page created from the template. Maximum 255 characters.description_html(string). Page body as HTML. Defaults to<p></p>.color(string). Page color, for example#3f76ff. Defaults to an empty string.parent(object). The page that new pages nest under, as{"id", "project_id", "project_identifier"}. All three keys are required whenparentis not empty. Defaults to{}.view_props(object). Page display settings, for example{"full_width": false}. Defaults to{}.logo_props(object). Editor-owned descriptor for the page icon or emoji. Defaults to{}.
See Parent validation.
short_description:optionalstringOne-line summary of what the template is for. Nullable. null and omission are both stored as an empty string.
description_html:optionalstringLonger description of the template itself, as HTML. Defaults to <p></p>. This is not the page body, which goes in template_data.description_html.
is_published:optionalbooleanWhether the template is published. Defaults to false.
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: created_at, created_by_id, description_html, id, is_published, name, project_id, short_description, short_id, slug, template_data, template_type, updated_at, updated_by_id.
See Sparse fields.
Scopes
templates.pages:write
Errors
| Status | Code | Cause |
|---|---|---|
400 | invalid_request | name, template_data, or template_data.name is missing, a value is over its length limit, or parent is malformed. |
401 | unauthorized | Missing or invalid credentials. |
402 | payment_required | Page templates aren't in your plan, or the feature is switched off for this workspace. |
403 | forbidden | Your role or token scope can't create workspace page templates. |
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 | 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. |
curl -X POST \
"https://api.plane.so/api/v2/workspaces/my-team/page-templates/" \
-H "X-Api-Key: $PLANE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Weekly sync",
"short_description": "Agenda and notes for the weekly team sync",
"description_html": "<p>Use this for the Monday team sync.</p>",
"template_data": {
"name": "Weekly sync notes",
"description_html": "<h2>Agenda</h2><ul><li><p>Updates</p></li><li><p>Blockers</p></li></ul>",
"color": "#3f76ff",
"view_props": { "full_width": false },
"logo_props": { "in_use": "emoji", "emoji": { "value": "128221" } }
}
}'import requests
response = requests.post(
"https://api.plane.so/api/v2/workspaces/my-team/page-templates/",
headers={"X-Api-Key": "your-api-key"},
json={
"name": "Weekly sync",
"short_description": "Agenda and notes for the weekly team sync",
"description_html": "<p>Use this for the Monday team sync.</p>",
"template_data": {
"name": "Weekly sync notes",
"description_html": "<h2>Agenda</h2><ul><li><p>Updates</p></li><li><p>Blockers</p></li></ul>",
"color": "#3f76ff",
"view_props": {"full_width": False},
"logo_props": {"in_use": "emoji", "emoji": {"value": "128221"}},
},
},
)
print(response.json())const response = await fetch("https://api.plane.so/api/v2/workspaces/my-team/page-templates/", {
method: "POST",
headers: {
"X-Api-Key": "your-api-key",
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Weekly sync",
short_description: "Agenda and notes for the weekly team sync",
description_html: "<p>Use this for the Monday team sync.</p>",
template_data: {
name: "Weekly sync notes",
description_html: "<h2>Agenda</h2><ul><li><p>Updates</p></li><li><p>Blockers</p></li></ul>",
color: "#3f76ff",
view_props: { full_width: false },
logo_props: { in_use: "emoji", emoji: { value: "128221" } },
},
}),
});
const data = await response.json();{
"id": "3d8b2f4e-6a1c-4e57-9b02-7c5d1e8f4a36",
"name": "Weekly sync",
"short_description": "Agenda and notes for the weekly team sync",
"description_html": "<p>Use this for the Monday team sync.</p>",
"template_type": "page",
"is_published": false,
"short_id": "k3x9p2",
"slug": "weekly-sync",
"project_id": null,
"template_data": {
"name": "Weekly sync notes",
"description_html": "<h2>Agenda</h2><ul><li><p>Updates</p></li><li><p>Blockers</p></li></ul>",
"color": "#3f76ff",
"parent": {},
"view_props": {
"full_width": false
},
"logo_props": {
"emoji": {
"value": "128221"
},
"in_use": "emoji"
}
},
"created_at": "2026-01-14T09:22:41.478363Z",
"updated_at": "2026-01-14T09:22:41.478363Z",
"created_by_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
"updated_by_id": null
}A parent needs all three keys
The API checks template_data.parent for shape only. A parent with only an id returns 400 invalid_request. Send id, project_id, and project_identifier together, or leave parent out.

