Skip to content

List workspace members ​

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

Return the workspace's active member roster as a paginated list. Use it to resolve a person to the member_id you assign work to, or poll it to keep a user directory in sync with Plane.

The workspace roster is the superset of every project roster: anyone on a project is on this list too.

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.

Query parameters ​

Filters combine with AND. Check your spelling on order_by and paginate. Neither is validated: an unrecognized order_by falls back to the default ordering, and anything other than paginate=cursor 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 look up several users at once, comma-separated, for example ?member_id__in=16c61a3a-512a-48ac-b0be-b6b46fe6f430,7f2b9e04-6c1d-4a58-9e3b-0d4c8a2f6b71.

This is a cheap way to check whether a person is still in the workspace, and their current role, without paging through the whole roster.

role:optionalstring

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

The value is a plain string, not a fixed enum, so custom roles are matched by their 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

Comma-separated relations to embed alongside the ids: member (the member's user object).

Expanded objects get their own 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 returns 400.

?fields= and ?expand= take separate sets of names. Relation names aren't valid in ?fields=, field names aren't valid in ?expand=, and field filtering never removes an expanded object: ?fields=id,name&expand=state returns id, name and state. See Expanding relations.

Ordering ​

order_by:optionalstring

Field to sort by. Prefix with - for descending.

  • created_at , -created_at: when the person joined the workspace
  • id , -id

There is no ordering by name or role; sort the page client-side, or expand and sort on display_name.

Pagination ​

per_page:optionalinteger

Page size. Defaults to 50, maximum 200.

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 keyset envelope (no COUNT query), which returns next_cursor and has_more instead of next and total_count. Use it when you page through a large workspace from start to finish.

count:optionalboolean

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

Response shaping ​

fields:optionalstring

Comma-separated list of fields to return on each row. Fields you don't request are left out of the response instead of coming back as null, so a missing key means you didn't ask for it and null means the value is null. id is always included, whether or not you name it.

Pass all to get every requestable field. An unknown field name returns 400 with the list of valid names and the closest match, so a typo is caught instead of being silently ignored.

Requestable here: id, member_id, role.

See Sparse fields.

Scopes ​

workspaces.members:read

Errors ​

StatusCodeCause
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 read this workspace roster.
404not_foundNo such workspace, or it's outside your tenant.
406not_acceptableThe Accept header asks for a representation the API can't produce.
429rate_limitedThrottled. Honor the Retry-After header before retrying.

Membership id versus user id

id identifies the membership row, and member_id identifies the person. Key caches and joins on member_id, because the same user has a different id on every roster they appear in. See Members overview.

Roles restrict writes, not this read

A role that is too narrow returns 403 on the write you attempted. A 409 work_item_types_managed_at_workspace or work_item_types_managed_at_project is not a role problem. See Work item type modes.

List workspace members
bash
curl -X GET \
  "https://api.plane.so/api/v2/workspaces/my-team/members/?expand=member&per_page=50" \
  -H "X-Api-Key: $PLANE_API_KEY"
Response200
json
{
  "data": [
    {
      "id": "c1b7e4a9-2f66-4a1d-9c3b-7d5e2f8a1b40",
      "member_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
      "role": "owner",
      "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": "3e8a5d17-9c40-4b2f-81d6-4a7f2b9e0c53",
      "member_id": "7f2b9e04-6c1d-4a58-9e3b-0d4c8a2f6b71",
      "role": "member",
      "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"
  }
}
Response200
json
{
  "data": [
    {
      "id": "c1b7e4a9-2f66-4a1d-9c3b-7d5e2f8a1b40",
      "member_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
      "role": "owner"
    },
    {
      "id": "3e8a5d17-9c40-4b2f-81d6-4a7f2b9e0c53",
      "member_id": "7f2b9e04-6c1d-4a58-9e3b-0d4c8a2f6b71",
      "role": "member"
    }
  ],
  "next": null,
  "previous": null,
  "total_count": 2,
  "pagination": {
    "style": "offset"
  }
}

Expanding a page costs one query, not one per row

?expand=member loads the users in bulk, so a 200-row page is a single extra query. Prefer one expanded page over 200 follow-up requests.