Skip to content

Create a project page template ​

POST/api/v2/workspaces/{slug}/projects/{project_id}/page-templates/

Create a page template that belongs to one project. The top-level fields describe the template in the picker, and template_data is the page that the template creates. The new template's project_id is the project in the path, and it stays there.

In a workspace with workspace governance turned on, this route returns 409 templates_managed_at_workspace and creates nothing. Use Create a workspace page template there. The governance check runs before body validation, so a governed workspace answers 409 even when the body is invalid.

Path Parameters ​

slug:requiredstring

The workspace slug. It appears in your Plane URLs, so in https://app.plane.so/my-team/projects/ the slug is my-team.

project_id:requiredstring

The project to add the template to. Accepts the project UUID or its identifier, for example ENG.

Body Parameters ​

name:requiredstring

Display name of the template, shown in the template picker. Maximum 255 characters.

template_data:requiredobject

The 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 when parent is 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:optionalstring

One-line summary of what the template is for. Nullable. null and omission are both stored as an empty string.

description_html:optionalstring

Longer 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:optionalboolean

Whether the template is published. Defaults to false.

Response shaping ​

fields:optionalstring

Comma-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 ​

StatusCodeCause
400invalid_requestname, template_data, or template_data.name is missing, a value is over its length limit, or parent is malformed.
401unauthorizedMissing or invalid credentials.
402payment_requiredPage templates aren't in your plan, or the feature is switched off for this workspace.
403forbiddenYour role or token scope can't create page templates in this project.
404not_foundNo such workspace or project, or it's outside your tenant.
406not_acceptableThe Accept header asks for a representation the API can't produce.
409templates_managed_at_workspaceThe workspace has governance turned on. Create the template at the workspace level instead.
409governance_migration_in_progressThe workspace is migrating to workspace governance. Writes are blocked until it finishes, so retry later.
413payload_too_largeThe request body is over the size limit.
415unsupported_media_typeThe Content-Type isn't one this endpoint accepts.
429rate_limitedThrottled. Honor the Retry-After header before retrying.
Create a project page template
bash
curl -X POST \
  "https://api.plane.so/api/v2/workspaces/my-team/projects/4af68566-94a4-4eb3-94aa-50dc9427067b/page-templates/" \
  -H "X-Api-Key: $PLANE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Incident postmortem",
  "short_description": "Timeline, impact, root cause, and follow-ups",
  "description_html": "<p>Fill this in within 48 hours of an incident.</p>",
  "template_data": {
    "name": "Postmortem",
    "description_html": "<h2>Timeline</h2><p></p><h2>Impact</h2><p></p><h2>Root cause</h2><p></p><h2>Follow-ups</h2><p></p>",
    "color": "#e5484d",
    "parent": {
      "id": "c2f5a8d1-7e3b-4c96-8a14-0b9d6e2f5c71",
      "project_id": "4af68566-94a4-4eb3-94aa-50dc9427067b",
      "project_identifier": "ENG"
    },
    "view_props": { "full_width": true }
  }
}'
Response201
json
{
  "id": "9e1c7a52-3b4d-4f86-a0d2-5c8e7b1f3a94",
  "name": "Incident postmortem",
  "short_description": "Timeline, impact, root cause, and follow-ups",
  "description_html": "<p>Fill this in within 48 hours of an incident.</p>",
  "template_type": "page",
  "is_published": false,
  "short_id": "m5v8c1",
  "slug": "incident-postmortem",
  "project_id": "4af68566-94a4-4eb3-94aa-50dc9427067b",
  "template_data": {
    "name": "Postmortem",
    "description_html": "<h2>Timeline</h2><p></p><h2>Impact</h2><p></p><h2>Root cause</h2><p></p><h2>Follow-ups</h2><p></p>",
    "color": "#e5484d",
    "parent": {
      "id": "c2f5a8d1-7e3b-4c96-8a14-0b9d6e2f5c71",
      "project_id": "4af68566-94a4-4eb3-94aa-50dc9427067b",
      "project_identifier": "ENG"
    },
    "view_props": {
      "full_width": true
    },
    "logo_props": {}
  },
  "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
}
Response409
json
{
  "type": "conflict",
  "code": "templates_managed_at_workspace",
  "detail": "Templates are managed at the workspace level."
}