Skip to content

Create a module ​

POST/api/v2/workspaces/{slug}/projects/{project_id}/modules/

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

The 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:requiredstring

Display name for the module, unique within the project. Maximum 255 characters.

description:optionalstring

Plain-text summary of what the module covers.

status:optionalstring

Where the module sits in its lifecycle.

  • backlog — Captured, not yet committed to
  • planned — Committed to but not started
  • in-progress — Actively being worked on
  • paused — Started, then put on hold
  • completed — Delivered
  • cancelled — Dropped without delivering

Defaults to planned when omitted. A value outside this list is a 400 invalid_request.

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

Ordering 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:optionalany

Free-form JSON object holding the icon Plane renders for the module.

external_id:optionalstring

Your system's identifier for this module, for sync and import correlation. Maximum 255 characters. Nullable.

external_source:optionalstring

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

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: archived_at, created_at, created_by_id, description, external_id, external_source, id, lead_id, logo_props, member_ids, name, sort_order, start_date, status, target_date.

See Sparse fields.

expand:optionalstring

Comma-separated relations to embed alongside the ids: lead (the module lead), members (the module members).

Expansion is separate-key: ?expand=state keeps state_id and adds a state object next to it, so an id is never replaced by an object. An unknown value is a 400.

?fields= and ?expand= are independent namespaces. Relation names are not valid ?fields= tokens (and vice versa), and an expanded object survives field filtering — ?fields=id,name&expand=state returns id, name and state. See Expanding relations.

Scopes ​

projects.modules:write

Errors ​

StatusCodeCause
400invalid_requestMissing name, a status outside the enum, a lead_id who isn't a project member, or a target_date before start_date.
401unauthorizedMissing or invalid credentials.
402payment_requiredThe feature this endpoint belongs to isn't enabled on your plan, or is switched off.
403forbiddenYour role or token scope can't create modules 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.
409conflictA module with this name already exists in the project.
413payload_too_largeThe request body is over the size limit.
415unsupported_media_typeThe Content-Type isn't one this endpoint accepts.
429rate_limitedThrottled. Wait for the interval in Retry-After and retry.
Create a module
bash
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"
}'
Response201
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"
}
Response409
json
{
  "type": "conflict",
  "code": "conflict",
  "detail": "A module with this name already exists in the project."
}