Skip to content

Update a cycle ​

PATCH/api/v2/workspaces/{slug}/projects/{project_id}/cycles/{pk}/

Change a cycle in place — rename it, reschedule it, or attach correlation ids after an import.

The update is partial. Fields you omit are left untouched, and omitting a field is not the same as sending null: send "end_date": null to clear a date, omit end_date to keep it.

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

pk:requiredstring (uuid)

The cycle to update.

Body Parameters ​

name:optionalstring

New display name, unique within the project. Maximum 255 characters. Renaming onto a name another cycle already holds returns 409 conflict.

description:optionalstring

Free-form description of what the cycle covers.

start_date:optionalstring (date-time)

New opening date-time, in ISO 8601. Send null to unschedule the start.

end_date:optionalstring (date-time)

New closing date-time, in ISO 8601. Send null to unschedule the end.

timezone:optionalstring

The IANA time zone the cycle's dates are interpreted in, for example America/New_York or UTC. Changing it re-anchors where the existing boundaries fall locally, so send it together with the dates when you are moving a cycle between regions. 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. Replaces the stored value outright — it is not merged key by key.

external_id:optionalstring

Your system's identifier for this cycle. Maximum 255 characters, nullable.

external_source:optionalstring

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

There is no PUT

v2 updates are PATCH only. A PUT to this path returns 405 method_not_allowed. Audit fields such as created_at, created_by_id, and owned_by_id are read-only — including them has no effect.

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_requestA 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 update cycles in this project.
404not_foundNo such cycle, wrong project, or the record is outside your tenant.
406not_acceptableThe Accept header asks for a representation the API can't produce.
409conflictAnother cycle in the project already uses this name.
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.
Update a cycle
bash
curl -X PATCH \
  "https://api.plane.so/api/v2/workspaces/my-team/projects/4af68566-94a4-4eb3-94aa-50dc9427067b/cycles/7c1f9a4e-3b6d-4a52-8f0c-2d9e6b18c3a7/" \
  -H "X-Api-Key: $PLANE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Sprint 24 (extended)",
  "end_date": "2026-01-26T00:00:00Z"
}'
Response200
json
{
  "id": "7c1f9a4e-3b6d-4a52-8f0c-2d9e6b18c3a7",
  "name": "Sprint 24 (extended)",
  "description": "Checkout rewrite and billing cleanup",
  "start_date": "2026-01-05T00:00:00Z",
  "end_date": "2026-01-26T00: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"
}
Response400
json
{
  "type": "invalid_request",
  "code": "invalid_request",
  "detail": "The request body failed validation.",
  "errors": [
    {
      "field": "timezone",
      "code": "invalid_choice",
      "message": "\"America/Atlantis\" is not a valid choice."
    }
  ]
}