Skip to main content

Members, Roles, and Scopes

Access in Agility is a triangle: a Member (who) holds a Role (what they may do) on a Scope (where). A role assigned on a scope applies to that scope's whole subtree; the effective role at any scope is the nearest assignment at or above it. Each member also carries a DefaultRole — the role used when membership is granted without naming one.

The asset model tells only part of this story. Member.Scopes (and its reverse, Scope.Members) records membership, and Member.DefaultRole the default — but the member-scope-role assignment itself is not an asset. No asset type carries it, so no rest-1.v1 or query.v1 query can return "what role does this member hold on this scope." Writing and reading assignments each have their own path.

Granting Membership at the Default Role​

Adding a scope to Member.Scopes grants that member their default role on the scope (and, by inheritance, its subtree):

{
"from": "Member:3907",
"update": { "Scopes": { "add": ["Scope:1071"] } }
}

Updating from the other side — Scope.Members — is equivalent.

Granting a Specific Role​

Naming the role takes one of two parameterized operations — the same assignment reached from either corner of the triangle:

  • Member.AssignToScopeWithRole with { Scope, Role, IsOwner }
  • Scope.AssignMemberWithRole with { Member, Role, IsOwner }
{
"from": "Member:1040",
"execute": {
"op": "AssignToScopeWithRole",
"args": { "Scope": "Scope:1503", "Role": "Role:3", "IsOwner": true }
}
}

The Bulk API's operations page covers parameterized execution in detail, including running the assignment against many members or scopes at once. Repeating the operation with a different role replaces the assignment; IsOwner additionally marks the member as an owner of the project.

Roles themselves are queryable assets (/rest-1.v1/Data/Role) — note their Name values return as localization tags (Role.Name'Project Lead), which resolve through the localization API like any display name.

Reading Assignments: the Project Roles API​

Because assignments aren't assets, reading them has a dedicated endpoint: api/ProjectRoles, grouped from either direction:

GET ~/api/ProjectRoles/ByMember?where=ID='Member:20'
GET ~/api/ProjectRoles/ByProject?where=ID='Scope:0'

Each row reports the effective role — inherited assignments included — plus isOwner and the member's defaultRole:

{
"member": { "oidToken": "Member:20", "name": "Administrator", "defaultRole": { "oidToken": "Role:1", "name": "System Admin" } },
"projectRoles": [
{ "isOwner": true, "project": { "oidToken": "Scope:0", "name": "System (All Projects)" }, "role": { "oidToken": "Role:1", "name": "System Admin" } }
]
}

Details worth knowing:

  • Filter tokens are relative to the grouping asset: where=ID='Member:20', not where=Member.ID='Member:20'. Each direction takes a second filter for the other side — projectswhere on ByMember, memberswhere on ByProject — plus page and sort.
  • A bare GET ~/api/ProjectRoles/ByMember (no parameters) returns the endpoint's own usage documentation as a RAML document.

Provision a Member​

Finding, creating and deactivating members is a common administrative automation, separate from the role and scope questions above.

Find an existing member — Username is unique, so a lookup returns at most one member:

{
"from": "Member",
"where": { "Username": "andre.agile" },
"select": ["Name", "Username", "Email"]
}

Email is not unique, so a where match on it can return more than one Member — bound the result with page if you use it as the lookup key instead.

Create a member, providing the required fields plus the fields most useful for administration:

{
"AssetType": "Member",
"Name": "Andre Agile",
"Username": "andre.agile",
"Nickname": "Andre",
"Password": "<initial-password>",
"Email": "andre.agile@example.com",
"IsCollaborator": false,
"DefaultRole": "Role:4"
}

See Creating assets for the full create-command shape, including resolving DefaultRole by name instead of OID token.

Update a member the same way as any other asset:

{
"from": "Member:30633",
"update": { "Phone": "555-555-1212" }
}

Deactivate a member with the Inactivate operation. The member loses system access (a sign-in that worked returns 401 afterwards) but stays queryable with AssetState Closed:

{ "from": "Member:30633", "execute": "Inactivate" }

See Invoking operations for the operation syntax in full.

Prefer Inactivate when someone leaves: the member keeps its place in history, so ownership and time-by-person reports stay accurate. Delete removes the member from every query that doesn't request deleted assets, historical ones included, and from Agility's by-member reports, so their past ownership and logged time stop being attributed to them. Delete only a member created by mistake, or one whose username you need to reuse. An inactive member can't be deleted; reactivate it first.