Create a module
Create a module in a project. Only name is required — everything else can be filled in later with a PATCH as the work firms up.
Module names must be unique within a project — reusing one returns 409 conflict.
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 to create the module in.
Body Parameters
name:requiredstringDisplay name for the module, unique within the project. Maximum 255 characters.
description:optionalstringPlain-text summary of what the module covers.
status:optionalstringWhere the module sits in its lifecycle.
backlog— Captured, not yet committed toplanned— Committed to but not startedin-progress— Actively being worked onpaused— Started, then put on holdcompleted— Deliveredcancelled— Dropped without delivering
Defaults to planned when omitted. A value outside this list is a 400 validation_error.
start_date:optionalstring (date)Date the module is scheduled to begin, as YYYY-MM-DD. Nullable.
target_date:optionalstring (date)Date the module is expected to land, as YYYY-MM-DD. Nullable, and must not be earlier than start_date.
lead_id:optionalstring (uuid)The user accountable for the module. Must be a member of this project — any other user id is rejected with a 400 naming lead_id, never linked silently. Nullable.
sort_order:optionalnumberOrdering weight used when modules are listed. Lower values sort first. In a project that already has modules, Plane positions the new module ahead of them on create, so send sort_order in a follow-up PATCH if you need a specific slot.
logo_props:optionalanyFree-form JSON object holding the icon Plane renders for the module.
external_id:optionalstringYour system's identifier for this module, for sync and import correlation. Maximum 255 characters. Nullable.
external_source:optionalstringThe system external_id came from, for example github or jira. Maximum 255 characters. Nullable.
Members aren't set here
member_ids is read-only in v2, so a module is created with no members. Module membership is not yet writable through the v2 API.
Scopes
projects.modules:write
Errors
| Status | Code | Cause |
|---|---|---|
400 | validation_error | Missing name, a status outside the enum, a lead_id who isn't a project member, or a target_date before start_date. |
401 | unauthorized | Missing or invalid credentials. |
403 | forbidden | Your role or token scope can't create modules in this project. |
404 | resource_not_found | No such workspace or project, or it's outside your tenant. |
409 | conflict | A module with this name already exists in the project. |
429 | rate_limited | Throttled. Wait for the interval in Retry-After and retry. |
curl -X POST \
"https://api.plane.so/api/v2/workspaces/my-team/projects/4af68566-94a4-4eb3-94aa-50dc9427067b/modules/" \
-H "X-Api-Key: $PLANE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Billing revamp",
"description": "Rework subscription billing end to end.",
"status": "in-progress",
"start_date": "2026-01-05",
"target_date": "2026-02-27",
"lead_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430"
}'import requests
response = requests.post(
"https://api.plane.so/api/v2/workspaces/my-team/projects/4af68566-94a4-4eb3-94aa-50dc9427067b/modules/",
headers={"X-Api-Key": "your-api-key"},
json={
"name": "Billing revamp",
"description": "Rework subscription billing end to end.",
"status": "in-progress",
"start_date": "2026-01-05",
"target_date": "2026-02-27",
"lead_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
},
)
print(response.json())const response = await fetch(
"https://api.plane.so/api/v2/workspaces/my-team/projects/4af68566-94a4-4eb3-94aa-50dc9427067b/modules/",
{
method: "POST",
headers: {
"X-Api-Key": "your-api-key",
"Content-Type": "application/json",
},
body: JSON.stringify({
name: "Billing revamp",
description: "Rework subscription billing end to end.",
status: "in-progress",
start_date: "2026-01-05",
target_date: "2026-02-27",
lead_id: "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
}),
}
);
const data = await response.json();{
"id": "7c1f3d90-2a64-4e58-9b0d-3fa1c7e28b45",
"name": "Billing revamp",
"description": "Rework subscription billing end to end.",
"status": "in-progress",
"start_date": "2026-01-05",
"target_date": "2026-02-27",
"lead_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
"member_ids": [],
"sort_order": 65535.0,
"logo_props": {},
"external_id": null,
"external_source": null,
"archived_at": 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 module with this name already exists in the project."
}
