Skip to content

Create a project template ​

POST/api/v2/workspaces/{slug}/project-templates/

Create a project template in the workspace. The top-level fields describe the template in the picker, and template_data describes the project it creates, including its starter work items in template_data.workitems.

The API checks the shape of the JSON keys in template_data before it saves anything. A failure returns 400 invalid_request, and no template is created. See Shape checks.

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.

Body Parameters ​

name:requiredstring

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

template_data:requiredobject

The project seed. name is the only required key. It names the project, holds at most 255 characters, and must be unique among the workspace's project templates. Every other key is optional and takes its default when omitted:

  • Project fields: description, network (0 or 2), priority (none, low, medium, high, or urgent), timezone, archive_in and close_in (0 to 12), start_date, target_date, logo_props, cover_asset, default_assignee, and project_lead.
  • Feature toggles: module_view, cycle_view, issue_views_view, page_view, intake_view, is_time_tracking_enabled, is_issue_type_enabled, is_project_updates_enabled, is_epic_enabled, is_workflow_enabled, is_milestone_enabled, and guest_view_all_features.
  • Seeds: states, project_state, labels, estimates, workitem_types, epics, members, intake_settings, workflows, milestones, modules, recurring_work_items, and automations.
  • workitems: the starter work items. Each entry needs name (at most 255 characters) and takes description_html (defaults to <p></p>), priority (defaults to none), state, assignees, labels, type, modules, and properties.

Give each state, label, and other seed entry its own id, a UUID you choose. Work item seeds refer to them by that id, for example "state": {"id": "<a states[].id>", "name": "Backlog", "group": "backlog"}. The overview lists every key with its shape and default.

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>.

is_published:optionalboolean

Whether the template is published. Defaults to false. Other workspaces can copy a template only once it is published.

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

StatusCodeCause
400invalid_requestA required field is missing, a value is outside its allowed set or range, or a template_data key or work item seed fails the shape check.
401unauthorizedMissing or invalid credentials.
402payment_requiredProject templates aren't in your plan, or the feature is switched off for this workspace.
403forbiddenYour role or token scope can't create project templates.
404not_foundNo such workspace, or it's outside your tenant.
406not_acceptableThe Accept header asks for a representation the API can't produce.
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 template
bash
curl -X POST \
  "https://api.plane.so/api/v2/workspaces/my-team/project-templates/" \
  -H "X-Api-Key: $PLANE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "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_data": {
    "name": "Service project",
    "description": "Bootstraps a new backend service",
    "network": 2,
    "priority": "high",
    "states": [
      { "id": "a1f4c7e2-5b8d-4e19-9c63-2f7a0d4b8e15", "name": "Backlog", "color": "#a3a3a3", "sequence": 15000, "group": "backlog", "default": true },
      { "id": "c8e3f1b5-7a24-4d96-b0e1-4a6d8c2f9b37", "name": "Done", "color": "#16a34a", "sequence": 25000, "group": "completed", "default": false }
    ],
    "labels": [
      { "id": "d4b7a2e9-1f63-4c85-9e02-6b3d8f1a7c54", "name": "infra", "color": "#3f76ff" }
    ],
    "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" }
      }
    ]
  }
}'
Response201
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": false,
  "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": "high",
    "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": []
      }
    ]
  },
  "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
}

The shape check's errors differ from the usual list

Most 400 invalid_request responses carry errors as a list of {field, code, message} entries. A failed shape check carries errors as an object keyed by the failing template_data key instead. Branch on code and show detail, which names the work item seed by its index when one fails.