Skip to content

List project members

GET/api/v2/workspaces/{slug}/projects/{project_id}/members/

Return one project's active member roster as a paginated list. Reach for this when you need the people who can actually be assigned work in a project — the workspace roster is wider, and includes members with no access to this project.

Roles here are project roles, so the same person can come back as member on the workspace roster and as contributor on this one.

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 whose roster you want. A project id from another workspace returns 404.

Query Parameters

Filters combine with AND. Check your spelling on order_by and paginate — neither is validated. An unrecognized order_by value silently falls back to the default ordering, and anything other than paginate=cursor silently uses offset pagination. A typo shows up as an unexpected sort order or envelope, not as an error.

member_id:optionalstring (uuid)

Return the membership for one user. Use member_id__in to check several at once, comma-separated.

Pairing ?member_id=<user> with ?per_page=1 is the cheapest membership check there is: an empty data array means that user is not on this project.

role:optionalstring

Return only members holding this role slug, for example ?role=contributor. Use role__in for several roles at once, comma-separated.

The value is a plain string, not a fixed enum — a custom role is matched by its own slug.

search:optionalstring

A search term matched against the member's user record, so you can find someone by name or email without expanding first.

Expansion

expand:optionalstring

Set to member to embed the user beside the id. member_id stays in place and a member object with id, display_name, avatar_url, and email is added next to it.

member is the only accepted value; anything else returns 400. See Expanding relations.

Ordering

order_by:optionalstring

Field to sort by. Prefix with - for descending.

  • created_at , -created_at — when the person was added to the project
  • id , -id

Pagination

per_page:optionalinteger

Page size. Defaults to 50, maximum 200. Most project rosters fit in a single page.

offset:optionalinteger

Number of rows to skip from the start of the result set. Maximum 10000. Read next from the response rather than computing offsets yourself.

paginate:optionalstring

Set to cursor for the COUNT-free keyset envelope, which returns next_cursor and has_more instead of next and total_count.

count:optionalboolean

Defaults to true. Set to false to skip the COUNT(*) behind total_count; the field is then omitted from the response.

Scopes

projects.members:read

Errors

StatusCodeCause
401unauthorizedMissing or invalid credentials.
403forbiddenYour role or token scope can't read this project's roster.
404resource_not_foundNo such workspace or project, or it's outside your tenant.
429rate_limitedThrottled. Honor the Retry-After header before retrying.

Validate assignees against this list, not the workspace one

A member_id that is on the workspace roster but not on this project is not a valid assignee for the project's work items. Check membership here before writing assignee_ids.

A 409 is not a permission signal

403 means the caller's role or scope is too narrow. 409 work_item_types_managed_at_workspace or work_item_types_managed_at_project means the write belongs on the other surface, whatever the caller's role is. See Work item type modes.

List project members
bash
curl -X GET \
  "https://api.plane.so/api/v2/workspaces/my-team/projects/4af68566-94a4-4eb3-94aa-50dc9427067b/members/?expand=member" \
  -H "X-Api-Key: $PLANE_API_KEY"
Response200
json
{
  "data": [
    {
      "id": "9d2f5c81-4b73-4e60-8a1f-2c9b6d3e7f04",
      "member_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
      "role": "contributor",
      "member": {
        "id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
        "display_name": "Priya Raghavan",
        "avatar_url": "https://assets.plane.so/avatars/16c61a3a.png",
        "email": "priya@example.com"
      }
    },
    {
      "id": "b5c07e29-1d84-4f36-9a72-6e13c8d5a0f7",
      "member_id": "7f2b9e04-6c1d-4a58-9e3b-0d4c8a2f6b71",
      "role": "release-manager",
      "member": {
        "id": "7f2b9e04-6c1d-4a58-9e3b-0d4c8a2f6b71",
        "display_name": "Devansh Kapoor",
        "avatar_url": null,
        "email": "devansh@example.com"
      }
    }
  ],
  "next": null,
  "previous": null,
  "total_count": 2,
  "pagination": {
    "style": "offset"
  }
}
Response404
json
{
  "type": "https://api.plane.so/errors/resource_not_found",
  "title": "Not Found",
  "status": 404,
  "code": "resource_not_found",
  "detail": "No Project matches the given query."
}

Custom roles appear as their own slug

release-manager above is a custom role defined by the workspace. role has no enum in the schema, so treat any slug you do not recognize as a role you have no rules for rather than as bad data.