# Members

> People, roles and invitations in the organization and its workspaces

`client.members` and `client.invitations` do what the dashboard's Members pages
do: list who has access and at which role, give or take a role, and invite
people by email. Without `project=` a call is about the organization; with a
workspace ID, about that workspace.

An API key needs the opt-in `members:read` scope to read and `members:write` to
change; no default key and no `*` key holds either (see
[Admin scopes](https://junction-docs.qualiastudios.dev/cli/overview.md#admin-scopes)). Reading a space also takes
`projects:read`. A key acts as the person who made it: only an admin or owner
manages people, and nobody grants a role above their own. A workspace key acts
only on its own workspace, so it always passes `project=`. Each change shows in
Activity with the key's name.

## List Members

```python
people = client.members.list()                       # the organization
people = client.members.list(project="<PROJECT_ID>")  # one workspace
for m in people.people:
    print(m.email, m.role, [p.source for p in m.paths])
print(people.my_role, people.can_manage_members)
```

**Response**

```json title="MemberList"
{
"space": {"space_id": "…", "kind": "organization", "name": "Qualia", "slug": "qualia"},
"people": [
  {
    "user_id": "…",
    "email": "bob@example.com",
    "display_name": "Bob",
    "role": "admin",
    "paths": [
      {"role": "admin", "source": "direct", "grant_id": "…",
       "granted_on": {"space_id": "…", "kind": "organization", "name": "Qualia"}}
    ]
  }
],
"can_manage_members": true,
"can_create_children": true,
"my_role": "owner"
}
```

`can_manage_members` says whether this caller may change people there: for a
key, its creator's role allows it and the key holds `members:write`.

## Get One Person

One person's role on every space of the organization you can read. Needs an
organization key.

```python
person = client.members.get("<USER_ID>")
for space in person.spaces:
    print(space.name, space.kind, space.role, space.direct)
```

Returns a `PersonAccess`: `member` is false for a guest of a workspace.

## Set a Role

Give a person a role on the organization, or on one workspace, or move them to
another. An organization role is for the organization's members: invite anyone
else. A workspace role raises a member above their organization role there, or
makes someone a guest of it.

```python
change = client.members.set_role("<USER_ID>", "contributor")
change = client.members.set_role("<USER_ID>", "admin", project="<PROJECT_ID>")
print(change.from_role, change.role, change.effective_role, change.changed)
```

Returns a `RoleChange`. `changed` is false when they already held that role.

## Remove Someone

Take a person's role on one workspace away (they keep what the organization
gives them), or remove them from the organization. `keep=` names the workspaces
they stay a guest of, at the role they hold there. A key cannot remove its own
creator from the organization: leaving one takes a signed-in session.

```python
client.members.remove("<USER_ID>", project="<PROJECT_ID>")
change = client.members.remove("<USER_ID>", keep=["<PROJECT_ID>"])
print(change.kept)
```

## Invitations

```python
for inv in client.invitations.list():  # every one in the organization
    print(inv.email, inv.space_role, inv.space_name, inv.expires_at)

inv = client.invitations.send("new@example.com", "viewer", project="<PROJECT_ID>")
client.invitations.withdraw(inv.invitation_id)
```

`send` invites to the organization, or with `project=` to one workspace as a
guest, never above your own role. Someone already in the organization gets a
role with `set_role` instead (400). `withdraw` finds the invitation anywhere in
the organization; a workspace key passes its own workspace as `project=`.

## Errors

| Status | Meaning                                                                        |
| ------ | ------------------------------------------------------------------------------ |
| 403    | The key lacks `members:read` or `members:write`, or the role does not allow it |
| 404    | No such person, invitation or space, or not one you can read                   |
| 400    | Invite them first: they are not in the organization                            |
| 409    | The organization's last owner stays                                            |
