Skip to content

Create a cycle ​

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

Add a cycle to a project. Only name is required — leave the dates off to create an unscheduled cycle and fill them in later with Update a cycle.

Cycle 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 add the cycle to.

Body Parameters ​

name:requiredstring

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

description:optionalstring

Free-form description of what the cycle covers.

start_date:optionalstring (date-time)

When the cycle opens, as an ISO 8601 date-time. Nullable — omit it or send null for an unscheduled cycle.

end_date:optionalstring (date-time)

When the cycle closes, as an ISO 8601 date-time. Nullable.

timezone:optionalstring

The IANA time zone the cycle's dates are interpreted in, for example America/New_York, Asia/Kolkata, Europe/London, or UTC. Set it to the team's working zone so a cycle boundary lands at local midnight instead of UTC midnight. Any value outside the IANA list is rejected with 400 invalid_request.

sort_order:optionalnumber

Ordering weight for the cycle within the project. Lower values sort first when you list with ?order_by=sort_order.

logo_props:optionalany

JSON blob holding the cycle's icon configuration, as written by Plane clients. Stored and returned unchanged.

external_id:optionalstring

Your system's identifier for this cycle, for sync and import correlation. Maximum 255 characters. You can find the cycle again later with ?external_id= on List cycles.

external_source:optionalstring

The system external_id came from, for example jira or linear. Maximum 255 characters.

Owner is not settable

owned_by_id is returned on the response but is not a body parameter — Plane assigns cycle ownership and the API does not accept an override.

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, end_date, external_id, external_source, id, logo_props, name, owned_by_id, sort_order, start_date, timezone.

See Sparse fields.

expand:optionalstring

Comma-separated relations to embed alongside the ids: owned_by (the cycle owner).

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.cycles:write

Errors ​

StatusCodeCause
400invalid_requestMissing name, a name over 255 characters, an unparseable date, or a timezone outside the IANA list.
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 cycles 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 cycle 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. Honor the Retry-After header before retrying.
Create a cycle
bash
curl -X POST \
  "https://api.plane.so/api/v2/workspaces/my-team/projects/4af68566-94a4-4eb3-94aa-50dc9427067b/cycles/" \
  -H "X-Api-Key: $PLANE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Sprint 24",
  "description": "Checkout rewrite and billing cleanup",
  "start_date": "2026-01-05T00:00:00Z",
  "end_date": "2026-01-19T00:00:00Z",
  "timezone": "America/New_York"
}'
Response201
json
{
  "id": "7c1f9a4e-3b6d-4a52-8f0c-2d9e6b18c3a7",
  "name": "Sprint 24",
  "description": "Checkout rewrite and billing cleanup",
  "start_date": "2026-01-05T00:00:00Z",
  "end_date": "2026-01-19T00:00:00Z",
  "timezone": "America/New_York",
  "owned_by_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
  "sort_order": 65535,
  "logo_props": {},
  "external_id": null,
  "external_source": 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 cycle with this name already exists in this project."
}