Skip to content

Create a recurring work item ​

POST/api/v2/workspaces/{slug}/projects/{project_id}/recurring-work-items/

Create a schedule that adds a work item to the project on a repeating date. Send both parts in one flat body: the blueprint, which is the work item to create, and the schedule, which says when to create it.

The response includes the first next_scheduled_at. If start_at is today, Plane creates the first work item right away. See How runs are scheduled.

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 schedule in. Every work item it creates goes into this project.

Body Parameters ​

The schedule:

start_at:requiredstring (date or date-time)

The first date a run can happen, for example 2026-08-03. Only the date counts. Plane stores it in UTC as 00:05 on that date in the project's timezone. Plane also accepts a full date-time and drops its time.

end_at:optionalstring (date or date-time) or null

The last date a run can happen. Only the date counts. Plane stores it as 23:59:59 on that date in the project's timezone. It must be after start_at. Because Plane stores start_at as 00:05 and end_at as 23:59:59, the same date as start_at is allowed. An earlier date returns 400 invalid_request. Omit it or send null for a schedule with no end.

interval_type:optionalstring

How often to run. One of daily, weekly, monthly, or yearly. Defaults to monthly. Any other value returns 400 invalid_request.

interval_count:optionalinteger

How many intervals between runs. 2 with weekly means every two weeks. Defaults to 1. Values below 1 return 400 invalid_request.

The blueprint:

name:requiredstring

The title each created work item gets. Maximum 255 characters.

description_html:optionalstring

The description each created work item gets, as HTML. Defaults to <p></p>.

priority:optionalstring

One of none, low, medium, high, or urgent. Defaults to none.

state_id:optionalstring (uuid) or null

The state each created work item starts in. It must be a state of the path project. In a workspace with governance turned on, states belong to the workspace catalog, so any state_id returns 400.

type_id:optionalstring (uuid) or null

The work item type each created work item gets. It must be one of the path project's work item types.

assignee_ids:optionalarray of string (uuid)

Users to assign each created work item to. Each must be an active member of the path project. Duplicates are dropped.

label_ids:optionalarray of string (uuid)

Work item labels to apply. Each must be a label of the path project. Duplicates are dropped.

module_ids:optionalarray of string (uuid)

Modules to add each created work item to. Each must be a module of the path project. Duplicates are dropped.

enabled, next_scheduled_at, last_run_at, and schedule_description are read-only. Plane ignores them if you send them.

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: assignee_ids, created_at, created_by_id, description_html, enabled, end_at, id, interval_count, interval_type, label_ids, last_run_at, module_ids, name, next_scheduled_at, priority, schedule_description, start_at, state_id, type_id, updated_at, updated_by_id, work_item_blueprint_id.

See Sparse fields.

Scopes ​

projects.recurring_work_items:write

Errors ​

StatusCodeCause
400invalid_requestMissing name or start_at, end_at before start_at, a bad interval_type or interval_count, or an id that doesn't belong to the project.
401unauthorizedMissing or invalid credentials.
402payment_requiredThe workspace's plan doesn't include recurring work items.
403forbiddenYour project role or token scope can't create recurring work items.
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.
409governance_migration_in_progressA governance migration is running, so the workspace is read-only. Retry after it finishes.
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 recurring work item
bash
curl -X POST \
  "https://api.plane.so/api/v2/workspaces/my-team/projects/4af68566-94a4-4eb3-94aa-50dc9427067b/recurring-work-items/" \
  -H "X-Api-Key: $PLANE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Weekly status report",
  "description_html": "<p>Summarize progress, risks, and next steps.</p>",
  "priority": "medium",
  "state_id": "2c4d16f8-9b3e-4a52-8d71-1f0e6c9a5b48",
  "assignee_ids": ["16c61a3a-512a-48ac-b0be-b6b46fe6f430"],
  "label_ids": ["c1b8f3d6-9a44-4e12-8f7a-2b6d5c9e1a03"],
  "start_at": "2026-08-03",
  "end_at": "2026-12-31",
  "interval_type": "weekly",
  "interval_count": 1
}'
Response201
json
{
  "id": "f3a8c1d6-2e5b-4a79-8c04-1b7e9d6f2a53",
  "work_item_blueprint_id": "a9d2e6f1-7c3b-4085-b4e8-2f0c6a1d9e37",
  "name": "Weekly status report",
  "description_html": "<p>Summarize progress, risks, and next steps.</p>",
  "priority": "medium",
  "state_id": "2c4d16f8-9b3e-4a52-8d71-1f0e6c9a5b48",
  "type_id": null,
  "assignee_ids": ["16c61a3a-512a-48ac-b0be-b6b46fe6f430"],
  "label_ids": ["c1b8f3d6-9a44-4e12-8f7a-2b6d5c9e1a03"],
  "module_ids": [],
  "start_at": "2026-08-03T00:05:00Z",
  "end_at": "2026-12-31T23:59:59Z",
  "interval_type": "weekly",
  "interval_count": 1,
  "next_scheduled_at": "2026-08-03T00:05:00Z",
  "last_run_at": null,
  "enabled": true,
  "schedule_description": "Every week on Monday at 00:05",
  "created_at": "2026-07-28T09:22:41.478363Z",
  "updated_at": "2026-07-28T09:22:41.478363Z",
  "created_by_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
  "updated_by_id": null
}
Response400
json
{
  "type": "invalid_request",
  "code": "invalid_request",
  "detail": "One or more fields failed validation.",
  "errors": [
    {
      "field": "end_at",
      "code": "invalid",
      "message": "end_at must be after start_at."
    }
  ]
}
Response400
json
{
  "type": "invalid_request",
  "code": "invalid_request",
  "detail": "One or more fields failed validation.",
  "errors": [
    {
      "field": "state_id",
      "code": "invalid",
      "message": "State does not belong to this project."
    }
  ]
}

The examples use a project in UTC

Plane pins start_at and end_at in the project's timezone and returns them in UTC. In a project set to America/New_York, the same request stores start_at as 2026-08-03T04:05:00Z.