Skip to content

Update a permission grant ​

PATCH/api/v2/workspaces/{slug}/permissions/grants/{pk}/

Change a grant's relation, its extra or refused permissions, or its expiry. The subject and the resource are the grant's identity and can't change. To move access to another subject or resource, delete this grant and create a new one. Body keys other than the four below are ignored.

PATCH is partial. A field you omit keeps its current value. A list you send replaces the whole list, so send the complete set you want.

You need the <resource_type>:manage permission on the grant's resource. Otherwise the response is 403. A grant that has expired or been deleted returns 404.

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.

pk:requiredstring (uuid)

The id of the grant to update.

Body Parameters ​

Every field is optional. Send the subset you're changing.

relation:optionalstring

The new role slug, for example contributor. Maximum 50 characters. Plane doesn't check the value against the workspace's roles. Read valid slugs from List roles.

permissions_grant:optionalarray of string

The complete list of extra permissions. Each entry is a permission such as workitem:archive, or one of the wildcards * and <resource_type>:*. Send [] to remove them all. An entry that isn't a real permission returns 400.

permissions_deny:optionalarray of string

The complete list of refused permissions. Same format and validation as permissions_grant. Send [] to remove them all.

expires_at:optionalstring (date-time)

When the grant expires. Send null to make the grant permanent.

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, expires_at, id, permissions_deny, permissions_grant, relation, resource_id, resource_type, subject_id, subject_type, updated_at.

See Sparse fields.

Scopes ​

workspaces.permissions:write

Errors ​

StatusCodeCause
400invalid_requestA permission string is invalid, relation is too long, or expires_at isn't a date-time.
401unauthorizedMissing or invalid credentials.
402payment_requiredThe feature this endpoint belongs to isn't enabled on your plan, or is switched off.
403forbiddenYou can't manage access to the grant's resource, or your token scope doesn't allow this.
404not_foundNo such grant in this workspace, or it has expired or been deleted.
406not_acceptableThe Accept header asks for a representation the API can't produce.
409conflictNot raised by this endpoint's own checks.
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 permission grant
bash
curl -X PATCH \
  "https://api.plane.so/api/v2/workspaces/my-team/permissions/grants/e2f9c4a7-1b8d-4c63-9e05-7a3b6d1f8c94/" \
  -H "X-Api-Key: $PLANE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "relation": "contributor",
  "permissions_grant": [],
  "expires_at": null
}'
Response200
json
{
  "id": "e2f9c4a7-1b8d-4c63-9e05-7a3b6d1f8c94",
  "subject_type": "user",
  "subject_id": "a8d3e5f1-6b2c-4e97-8d40-1c5f7a9b3e26",
  "relation": "contributor",
  "resource_type": "project",
  "resource_id": "4af68566-94a4-4eb3-94aa-50dc9427067b",
  "permissions_grant": [],
  "permissions_deny": [],
  "expires_at": null,
  "created_at": "2026-01-20T11:05:17.204918Z",
  "updated_at": "2026-02-03T16:45:12.904117Z"
}
Response404
json
{
  "type": "not_found",
  "code": "not_found",
  "detail": "The requested resource was not found."
}