Permission grants overview
A permission grant gives a user or a teamspace a relation on one resource, for example "Ravi is a guest on the Mobile project" or "the Platform teamspace is a contributor on the Mobile project". The relation is a role slug, and the role decides what the holder can do. A grant can also add permissions on top of the role, refuse permissions the role would allow, and expire at a set time.
A subject has at most one grant on a given resource, so creating a grant for a subject that already has one replaces it.
Membership is stored as grants too
Plane writes a grant for each workspace, project, and teamspace membership, so the grants on a project include one per member. Changing or deleting those grants here doesn't update the member records. Manage members with the Members endpoints.
To read your own permissions, use the Permissions reads instead.
Learn more about roles and permissions
The permission grant object
Attributes
idstring (uuid)Unique identifier for the grant.
subject_typestringWho holds the grant.
userfor one user, orteamspacefor every member of a teamspace. Plane applies a teamspace grant to the teamspace's members onprojectresources.subject_idstring (uuid)The id of the user or teamspace.
relationstringThe role the subject holds on the resource, as a role slug. On a project the system roles are
admin,contributor,commenter, andguest. On a workspace they areowner,admin,member, andguest. A custom role uses its own slug. See Roles.resource_typestringThe kind of resource, as the permission engine names it, for example
project,cycle, ormodule.resource_idstring (uuid)The id of the resource.
permissions_grantarray of stringExtra permissions on this resource beyond what the relation gives, as
<resource_type>:<action>strings such asworkitem:archive.permissions_denyarray of stringPermissions refused on this resource, even when the relation,
permissions_grant, or a parent's role would allow them.expires_atstring (date-time) or nullWhen the grant expires.
nullmeans it never expires. An expired grant no longer appears in list and get responses.created_atstring (date-time)When the grant was created.
updated_atstring (date-time)When the grant was last changed.
{
"id": "e2f9c4a7-1b8d-4c63-9e05-7a3b6d1f8c94",
"subject_type": "user",
"subject_id": "a8d3e5f1-6b2c-4e97-8d40-1c5f7a9b3e26",
"relation": "guest",
"resource_type": "project",
"resource_id": "4af68566-94a4-4eb3-94aa-50dc9427067b",
"permissions_grant": ["workitem:create"],
"permissions_deny": [],
"expires_at": "2026-03-31T23:59:59Z",
"created_at": "2026-01-14T09:22:41.478363Z",
"updated_at": "2026-01-14T09:22:41.478363Z"
}Endpoints
| Method | Path | Description |
|---|---|---|
GET | /api/v2/workspaces/{slug}/permissions/grants/ | List permission grants |
GET | /api/v2/workspaces/{slug}/permissions/grants/{pk}/ | Get a permission grant |
POST | /api/v2/workspaces/{slug}/permissions/grants/ | Create a permission grant |
PATCH | /api/v2/workspaces/{slug}/permissions/grants/{pk}/ | Update a permission grant |
DELETE | /api/v2/workspaces/{slug}/permissions/grants/{pk}/ | Delete a permission grant |
Who can manage grants
Every grant endpoint requires the <resource_type>:manage permission, checked on the resource the grant is for. For a project, that means a project admin, or a workspace owner or admin. Being a member of the workspace isn't enough. Without it, the request returns 403 forbidden with You don't have permission to manage access to this resource.
Only resource types that have a manage action can carry grants through this API, for example workspace, project, cycle, module, intake, initiative, and teamspace. For any other type, such as workitem, the check can't pass and every request returns 403.
Grants are listed per resource. List permission grants requires both resource_type and resource_id, and there is no workspace-wide listing.
| Operation | Scope |
|---|---|
| List and get | workspaces.permissions:read |
| Create, update, and delete | workspaces.permissions:write |
Response shaping
Grant reads and writes accept ?fields= for sparse responses. See Sparse fields. Errors follow the shared problem+json contract.
Changed from v1
- The list returns the standard paginated envelope instead of a bare array, and it now requires
<resource_type>:manageon the resource, as the writes always did. - Missing
resource_typeorresource_idon the list returns a400problem body instead of an{"error": ...}object. GET /permissions/grants/{pk}/is new. v1 had no read for a single grant.- Grants carry
updated_at.
See Migrating from v1 for the full list.

