Docs

Custom roles

Named sets of permissions, assigned to people and to brand groups.

View as Markdown

Every person in an organization holds one of the three predefined roles: owner, admin or member. A custom role is a named set of permissions added on top of one of them, so one person can hold exactly the one thing their job needs without becoming an admin. A custom role is a set of the same permission names an API key holds, and it is assigned to a person or to a brand group.

What a request may do

The effective permissions of a request are the union of the predefined role and every custom role the person holds, directly or through a group. Nothing is cached: the roles are read with the session on every request, so a role added or removed takes effect on the next call, not at the next sign in.

cURL
curl -s -X POST https://api.162-55-213-24.sslip.io/v0/roles \
  -H "Authorization: Bearer ***" \
  -H "Content-Type: application/json" \
  -d '{"name": "Support lead", "description": "Reads the team screen", "permissions": ["member_read"]}'

What a request can see

A custom role grants permissions, never visibility. What a person can open is decided by the grants on an inbox or on a domain, and a role does not touch them: a role listing message_read for somebody with no grant answers 200 with an empty list, not somebody else's mail. This is the same intersection a scoped API key is subject to, and it holds by construction rather than by a check somebody can forget.

Assigning to a person or to a group

A PUT replaces the whole set for one subject, so the request says what the person should hold rather than what to add: a role left out of role_ids is revoked. Both halves are audited, as role.assign and role.revoke.

cURL
# One person
curl -s -X PUT https://api.162-55-213-24.sslip.io/v0/members/USER_ID/roles \
  -H "Authorization: Bearer ***" \
  -H "Content-Type: application/json" \
  -d '{"role_ids": ["ROLE_ID"]}'

# One brand group
curl -s -X PUT https://api.162-55-213-24.sslip.io/v0/groups/GROUP_ID/roles \
  -H "Authorization: Bearer ***" \
  -H "Content-Type: application/json" \
  -d '{"role_ids": ["ROLE_ID"]}'

A group is a brand. Its members are the people who can already see mail in it: those holding a grant on one of its domains or on one of its inboxes. Membership is read from the grants rather than copied into the assignment, so granting somebody a domain of the group is what makes them a member, and withdrawing the grant takes the role away with it. Deleting the group, deleting the role, or removing the person all revoke the assignments in the same transaction, and each is audited with the count.

Sensitive permissions are never implicit

data_delete and message_release are not effects of full access, and a custom role is not a way around that: a role that lists one of them can only be created or assigned by somebody who holds it too. The refusal is 403 permission_escalation, the same code a key gets when it tries to issue a wider key. In practice an owner builds the erasing role and hands it out; an admin cannot.

Errors

StatusCodeMeaning
422unknown_permissionA name in permissions is not one of the permission names; the message lists the valid ones.
403permission_escalationThe role, or one of the roles being assigned, grants a permission the caller does not hold.
409already_existsAnother role of the organization already uses that name, compared ignoring case.
409limit_exceededThe organization holds 50 custom roles, which is the limit.
404not_foundThe role, the member or the group is not in this organization.

Endpoints

GET/v0/roles

List custom roles (requires member_read)

Responses

  • 200 OK: CustomRoleList
  • 403 Error envelope: Error

POST/v0/roles

Create a custom role (requires member_write, audited)

Body (CustomRoleRequest)

NameTypeNotes
namestringmax length 60
descriptionstringmax length 200
permissionsPermission[]Required on create. An unknown name is 422 unknown_permission; a name the caller does not hold is 403 permission_escalation.

Responses

  • 201 Created: CustomRole
  • 400 Error envelope: Error
  • 403 Error envelope: Error
  • 409 Error envelope: Error
  • 422 Error envelope: Error

PATCH/v0/roles/{role}

Change a role's name, description or permission set (requires member_write, audited)

Parameters

NameTypeNotes
rolestring (uuid)in path, required

Body (CustomRoleRequest)

NameTypeNotes
namestringmax length 60
descriptionstringmax length 200
permissionsPermission[]Required on create. An unknown name is 422 unknown_permission; a name the caller does not hold is 403 permission_escalation.

Responses

  • 200 OK: CustomRole
  • 400 Error envelope: Error
  • 403 Error envelope: Error
  • 404 Error envelope: Error
  • 409 Error envelope: Error
  • 422 Error envelope: Error

DELETE/v0/roles/{role}

Delete a custom role (requires member_write, audited)

Parameters

NameTypeNotes
rolestring (uuid)in path, required

Responses

  • 204 Deleted, assignments revoked
  • 403 Error envelope: Error
  • 404 Error envelope: Error

PUT/v0/members/{user}/roles

Replace the custom roles of a person (requires member_write, audited)

Parameters

NameTypeNotes
userstring (uuid)in path, required

Body (RoleAssignmentRequest)

NameTypeNotes
role_idsstring (uuid)[]required, The whole set the subject should hold. An empty array revokes every custom role from the subject; that is a revocation, not a mistake., at most 20 items

Responses

  • 200 OK: RoleAssignmentChange
  • 400 Error envelope: Error
  • 403 Error envelope: Error
  • 404 Error envelope: Error

PUT/v0/groups/{group}/roles

Replace the custom roles of a group (requires member_write, audited)

Parameters

NameTypeNotes
groupstring (uuid)in path, required

Body (RoleAssignmentRequest)

NameTypeNotes
role_idsstring (uuid)[]required, The whole set the subject should hold. An empty array revokes every custom role from the subject; that is a revocation, not a mistake., at most 20 items

Responses

  • 200 OK: RoleAssignmentChange
  • 400 Error envelope: Error
  • 403 Error envelope: Error
  • 404 Error envelope: Error

CustomRole object

NameTypeNotes
role_idstring (uuid)
namestring1 to 60 characters, unique in the organization ignoring case
descriptionstringAt most 200 characters; empty when none was given
permissionsPermission[]Names an API key could hold. A custom role that lists nothing is refused on the write path.
member_countintegerHow many people the role reaches now, counting a person once even when several of their groups carry it
assignmentsRoleAssignment[]The people and the groups that hold it
created_atstring (date-time)
updated_atstring (date-time)

CustomRoleRequest object

NameTypeNotes
namestringmax length 60
descriptionstringmax length 200
permissionsPermission[]Required on create. An unknown name is 422 unknown_permission; a name the caller does not hold is 403 permission_escalation.

RoleAssignment object

NameTypeNotes
role_idstring (uuid)
user_idstring (uuid)Set when the role is assigned to a person
emailstring (email)The address of that person, so the console does not resolve ids
group_idstring (uuid)Set when the role is assigned to a group
group_namestringThe brand's display name
assigned_atstring (date-time)

RoleAssignmentChange object

NameTypeNotes
assignmentsRoleAssignment[]
addedstring (uuid)[]Role ids this call gave
removedstring (uuid)[]Role ids this call took away