An identity can be a member of any project with some role and optionally variables assigned. This is called a membership. A project admin (and a superadmin) can manage project memberships by default, you can also setup Tenant ACL permissions for other user roles.

Creating a project membership

Add an existing identity to a project. For this operation you need to know an identity ID. If you want to add a user by an email check invite mutation

mutation {
  addProjectMember(
    projectSlug: "my-blog" 
    identityId: "2f673a53-af33-42b1-9e17-e1305fa26d9d"
    memberships: [
      {
        role: "editor",
        variables: [{name: "language", values: ["cs"]}]
      }
    ]
  ) {
    ok
    error {
      code
    } 
  }
}

a mutation can fail with following errors:

enum AddProjectMemberErrorCode {
  PROJECT_NOT_FOUND
  IDENTITY_NOT_FOUND
  VARIABLE_NOT_FOUND
  ALREADY_MEMBER
}

Updating a project membership

You can update existing member of an project using updateProjectMember mutation. Arguments and response structure of the mutation are the same as of addProjectMeber mutation. Only error codes differs:

enum UpdateProjectMemberErrorCode {
  PROJECT_NOT_FOUND
  VARIABLE_NOT_FOUND
  NOT_MEMBER
}

Revoking a project membership

You can remove a project member using following mutation

mutation {
  removeProjectMember(
    projectSlug: "my-blog" 
    identityId: "2f673a53-af33-42b1-9e17-e1305fa26d9d"
  ) {
    ok
    error {
      code
    } 
  }
}

Viewing project members

query {
  projectBySlug(slug: "my-blog") {
    name
    members {
      identity {
        id
        person {
          email
        }
      }
      memberships {
        role
        variables {
          name
          values
        }
      }
    }
  }
}

Global identity roles

Project memberships scope a person's access to a single project. Global roles (super_admin, project_admin, custom global roles defined in the tenant ACL — see Tenant ACL permissions) are attached directly to an identity and are not project-scoped. Two mutations grant and revoke them:

mutation {
  addGlobalIdentityRoles(
    identityId: "2f673a53-af33-42b1-9e17-e1305fa26d9d",
    roles: ["super_admin", "monitor"]
  ) {
    ok
    error { code developerMessage }
    result { identity { id roles } }
  }
}
mutation {
  removeGlobalIdentityRoles(
    identityId: "2f673a53-af33-42b1-9e17-e1305fa26d9d",
    roles: ["monitor"]
  ) {
    ok
    error { code }
    result { identity { id roles } }
  }
}

Errors:

CodeCause
IDENTITY_NOT_FOUNDNo such identity.
INVALID_ROLEA role in the list is not defined in the tenant ACL or the caller is not allowed to grant it.

Both are gated by identity:addGlobalRoles / identity:removeGlobalRoles against the role list — SUPER_ADMIN can grant any role, PROJECT_ADMIN is restricted to the project-admin allowed-input-roles set, and any other identity needs an explicit ACL grant scoping which roles it may assign. result.identity.roles echoes the new role set after the mutation.

Audit

Available since 2.2

Every successful addProjectMember, updateProjectMember, and removeProjectMember is recorded in the audit log as project_membership_create / project_membership_update / project_membership_remove, with a {before, after} snapshot of the affected memberships and target_person_id resolved from the affected identity.

Global role mutations (addGlobalIdentityRoles, removeGlobalIdentityRoles) are audited the same way as global_role_grant / global_role_revoke.