Project releases overview
A project release is a named, versioned deliverable with its own changelog. It is the same object as a workspace release, except that it belongs to one project. It can still hold work items from any project in the workspace. You manage it through the routes under /api/v2/workspaces/{slug}/projects/{project_id}/releases/.
Comments and links on a project release have their own routes, documented in Project release comments and Project release links.
Learn more about project releases
The project release object
Attributes
idstring (uuid)Unique identifier for the release.
namestringDisplay name. Maximum 255 characters. Names are unique across the whole workspace, so a project release can't reuse the name of a workspace release or of a release in another project.
project_idstring (uuid)The project the release belongs to. Set from the route when you create the release and read-only after that. A workspace release returns
nullhere.description_idstring (uuid)The id of the description record that holds the release body.
description_htmlstringThe release description as HTML. List rows leave it out unless you request it with
?fields=.statusstringWhere the release is in its lifecycle. One of
unreleased(the default),released, orcancelled.target_datestring (date) or nullThe planned ship date, as
YYYY-MM-DD.nulluntil you set it.release_datestring (date) or nullThe date the release shipped, as
YYYY-MM-DD.nulluntil you set it.lead_idstring (uuid) or nullThe user who leads the release. Must be an active member of the workspace.
nullwhen the release has no lead.tag_idstring (uuid) or nullThe version tag, from the workspace release tag catalog.
nullwhen the release has no tag.is_latestbooleanWhether the release is marked as the latest one. Plane doesn't limit the flag to one release, so setting it doesn't clear it on other releases.
is_prereleasebooleanWhether the release is marked as a pre-release.
external_id,external_sourcestring or nullCorrelation fields for sync and import. Together they map a release to a record in another system. Both are
nullunless you set them.label_idsarray of string (uuid)The release labels applied to the release. Change them with Add or remove project release labels.
created_atstring (date-time)When the release was created.
updated_atstring (date-time)When the release was last updated.
created_by_idstring (uuid)The user who created the release.
updated_by_idstring (uuid) or nullThe user who last updated the release.
nulluntil someone updates it.
{
"id": "3e8a1f52-7c4b-4d9e-a6f1-0b2c5d8e9f14",
"name": "Mobile 4.2.0",
"project_id": "4af68566-94a4-4eb3-94aa-50dc9427067b",
"description_id": "5d2b7e91-4a3c-4f68-9e0d-1c7a8b2f6e35",
"description_html": "<p>Offline mode and the new onboarding flow.</p>",
"status": "unreleased",
"target_date": "2026-02-27",
"release_date": null,
"lead_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
"tag_id": "a41c9e07-2f5d-4b83-8c6e-9d1f0a3b7c52",
"is_latest": false,
"is_prerelease": false,
"external_id": null,
"external_source": null,
"label_ids": ["7f3a2c19-8e4d-4b61-a5c7-2d9e0f1b8a46"],
"created_at": "2026-01-14T09:22:41.478363Z",
"updated_at": "2026-01-14T09:22:41.478363Z",
"created_by_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
"updated_by_id": null
}Endpoints
| Method | Path | Description |
|---|---|---|
GET | /api/v2/workspaces/{slug}/projects/{project_id}/releases/ | List project releases |
GET | /api/v2/workspaces/{slug}/projects/{project_id}/releases/{pk}/ | Get a project release |
POST | /api/v2/workspaces/{slug}/projects/{project_id}/releases/ | Create a project release |
PATCH | /api/v2/workspaces/{slug}/projects/{project_id}/releases/{pk}/ | Update a project release |
DELETE | /api/v2/workspaces/{slug}/projects/{project_id}/releases/{pk}/ | Delete a project release |
GET | /api/v2/workspaces/{slug}/projects/{project_id}/releases/{pk}/changelog/ | Get a project release changelog |
PATCH | /api/v2/workspaces/{slug}/projects/{project_id}/releases/{pk}/changelog/ | Update a project release changelog |
GET | /api/v2/workspaces/{slug}/projects/{project_id}/releases/{pk}/labels/ | List a project release's labels |
POST | /api/v2/workspaces/{slug}/projects/{project_id}/releases/{pk}/labels/ | Add or remove project release labels |
GET | /api/v2/workspaces/{slug}/projects/{project_id}/releases/{pk}/work-items/ | List a project release's work items |
POST | /api/v2/workspaces/{slug}/projects/{project_id}/releases/{pk}/work-items/ | Add or remove project release work items |
Project releases and workspace releases
Both kinds of release are the same object with the same fields. project_id tells them apart. It holds the project's id on a project release and null on a workspace release, and both the project routes and the workspace routes return it.
The two sets of routes never overlap:
- The project routes return only releases in the project named in the path. A workspace release id, or the id of a release in another project, returns
404. - The workspace routes return only workspace releases. A project release id on
/api/v2/workspaces/{slug}/releases/{pk}/returns404, and so do its comments and links on the workspace comment and link routes. - A release can't move between a project and the workspace, or to another project. The project comes from the path when you create the release, and no request body field changes it afterward.
Two things are shared across the workspace:
- Release names. A project release can't reuse any release name already taken in the workspace.
- Tags and labels. The v2 catalogs at
/releases/tags/and/releases/labels/list only workspace-level tags and labels. Project-scoped ones, created in the app or through v1, don't appear there, buttag_idand adding a label still accept their ids, including tags and labels scoped to another project. List a project release's labels returns project-scoped labels too.
Plan gating
Every project release route, including the comment and link routes, needs the project releases entitlement on your plan. Without it, the API returns 402 payment_required. This entitlement is separate from the one for workspace releases, so having one doesn't give you the other.
The tag and label catalogs at /releases/tags/ and /releases/labels/ need the workspace releases entitlement. With only project releases on your plan, they return 402 payment_required.
The workspace Releases setting (is_release_enabled in workspace features) gates only the creation of workspace releases. While the setting is off, creating a workspace release returns 402 payment_required. Project release routes don't check the setting, and they don't check the project's own Releases feature toggle either.
Permissions
The API checks permissions against your role in the release's project. For the checks on a single release and on its comments and links, a workspace admin passes without a role in the project.
| Request | Needs | Without it |
|---|---|---|
| List project releases | View releases in the project | 403 forbidden |
| Create a project release | Create releases in the project | 403 forbidden |
| Get a project release | View the release | 403 forbidden |
| Get the changelog, or list its labels or work items | No role check | Not checked. The work items list still leaves out work items you can't see |
| Update a release or its changelog, add or remove its labels or work items | Edit the release | 403 forbidden |
| Delete a release | Delete the release | 403 forbidden |
| List or get comments and links | View the release | 404 not_found |
| Create a comment or link | View and edit the release | 404 without view, 403 without edit |
| Update or delete a comment or link | Edit the release | 403 forbidden |
An id that doesn't exist in the project always returns 404, before any role check runs. When you can't view a release, the status depends on what you read. A read of the release itself returns 403, while reads of its comments and links return 404, as if the release didn't exist. Reads of its changelog, labels, and work items run no role check and return 200.
Find the work items in a release
There are two ways to read a release's work items:
- List a project release's work items returns full work item objects in one call. It takes no filters.
- The work items list with
?release_id=combines release membership with every other work item filter, ordering, and cursor pagination, for example?release_id={id}&state_group=started. It's available on the project work items list and the workspace work items list.
Use the child list for a quick read of everything in the release. Use the filter when you need to narrow, sort, or page through the set.
Response shaping
Every read that returns the release object accepts ?fields= for sparse responses and ?expand=lead,tag to embed the lead user and the tag next to their ids. See Sparse fields and Expanding relations. Errors follow the shared problem+json contract.
Changed from v1
- In v1, a project release's labels, work items, changelog, comments, and links were served on the workspace routes, under
/api/v1/workspaces/{slug}/releases/{release_id}/. In v2 they live under the project route, and the workspace routes return404for a project release. - Adding and removing labels or work items is one
POSTwith{"add": [...], "remove": [...]}, which returns{"added": [...], "removed": [...]}. v1 added withPOSTand removed withDELETE, passinglabel_idsorwork_item_ids. - Listing a release's work items returns full work item objects. v1 returned only
id,project_id, andnamefor each one. - v1 rejected work items from another project, and tags or labels scoped to another project, with
400andRELEASE_WORK_ITEM_OUT_OF_SCOPE,RELEASE_TAG_OUT_OF_SCOPE, orRELEASE_LABEL_OUT_OF_SCOPE. v2 accepts any work item, tag, or label in the workspace. - v1 created project-scoped tags and labels when you passed
projecton create, and listed them with?project_id=. v2 has no equivalent. Its tag and label catalogs create and list only workspace-level ones.
See Migrating from v1 for the full list.

