Roles

Read the default roles and create custom roles that grant a chosen set of permissions.

Before You Start

Read the following guides before proceeding:

GuideWhy
Getting StartedPlatform overview and setup
Api BasicsRequired headers and request configuration
PermissionsWhat each permission grants
EmployeesA role is assigned at invitation

Overview

A role is a named set of permissions. Every employee holds exactly one, and the corporation token
issued at their login carries the permissions that role granted at that moment.

Two kinds exist:

Default rolesCustom roles
is_globaltruefalse
ScopeEvery corporation on the platformOne corporation
SourceSeeded in the CorporateAccounts contractCreated through the API
EditableNoYes
DeletableNoYes
May grant SuYes — Owner doesNo

Default Roles

RoleRole idPermissions
Ownered7a5845-8727-4b06-9ba0-7d43ddc9e6aaSu
Admin31eba1db-0809-4d44-a479-0b696c4a2603CardSu, EmployeeSu, TransactionSu, AccountSu
Treasuryce3de0cc-bd42-4bc8-829c-e395fab506efTransactionSu, CardSu, AccountSu
HRa1b2c3d4-e5f6-0718-29a0-b1c2d3e4f506EmployeeSu
Member7386b48f-df9c-49fe-8ccc-38b5b0b55932TransactionCreate, TransactionView, CardView, AccountView, EmployeeView
Viewer240d596c-8973-4203-827f-a2c994bceafaTransactionView, CardView, AccountView, EmployeeView

Owner is assigned on-chain at registration and is not assignable through the API in the sense
that matters: the corporation always has exactly one owner, and
DELETE /api/v1/employees/{employeeId} refuses to remove them.

Admin and Treasury differ by one permission — EmployeeSu. Treasury can move money and manage
cards and accounts but cannot change who else can.


List Roles

GET /api/v1/roles

Requires EmployeeSu, EmployeeManage or EmployeeView. Returns the default roles and this
corporation's custom roles together.

Query parameters:

ParameterDescription
page_number1-indexed page number
page_sizePage size. Defaults to 25
sortname (default, ascending) or usage (descending usage count)

Response:

{
  "roles": [
    {
      "id": "7386b48f-df9c-49fe-8ccc-38b5b0b55932",
      "name": "Member",
      "permissions": ["TransactionCreate", "TransactionView", "CardView", "AccountView", "EmployeeView"],
      "is_global": true,
      "created_at": "2024-01-01T10:00:00Z",
      "updated_at": "2024-01-01T10:00:00Z"
    },
    {
      "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
      "name": "Finance manager",
      "permissions": ["TransactionCreate", "TransactionManage"],
      "is_global": false,
      "created_at": "2024-01-01T11:00:00Z",
      "updated_at": "2024-01-01T11:00:00Z"
    }
  ]
}
FieldDescription
idRole id. The value passed as role_id when inviting an employee or changing their role
nameDisplay name
permissionsPermission names granted by this role
is_globaltrue for a default role, false for a custom role
created_at / updated_atISO 8601 timestamps

Read One Role

GET /api/v1/roles/{roleId}

No permission is declared — any authenticated employee can read a role by id, which lets an employee
see what their own role grants.

Path parameters:

ParameterDescription
{roleId}Role id from GET /api/v1/roles, or the role_id claim of the corporation token

The response shape matches one item of GET /api/v1/roles.


Create a Custom Role

POST /api/v1/roles

Requires EmployeeSu.

Request body:

{
  "name": "Finance manager",
  "permissions": ["TransactionCreate", "TransactionManage"]
}
FieldTypeRequiredDescription
namestringYesDisplay name. Trimmed, must be non-empty and at most 64 bytes
permissionsarray of stringYesAt least one permission name. Duplicates are collapsed

Response:

{
  "id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "name": "Finance manager",
  "permissions": ["TransactionCreate", "TransactionManage"],
  "is_global": false,
  "created_at": "2024-01-01T11:00:00Z",
  "updated_at": "2024-01-01T11:00:00Z"
}

Accepted permission names:

Su · CardSu · CardCreate · CardViewDetails · CardManage · CardLimitManage · CardView ·
EmployeeSu · EmployeeManage · EmployeeView · TransactionSu · TransactionCreate ·
TransactionManage · TransactionView · AccountSu · AccountCreate · AccountManage ·
AccountView

Su is recognised but refused for custom roles. Including it returns 400 with
Su permission is not allowed for custom roles. The whole request is rejected — the other
permissions in the list are not created.

Permission names are case-sensitive and not trimmed of case variation. transactioncreate is
rejected with Unknown permission: transactioncreate. Surrounding whitespace is trimmed; an entry
that is only whitespace is rejected with Permission name must not be empty.

The name limit is 64 bytes, not 64 characters. A name in a multi-byte script reaches the limit
sooner than its character count suggests.

Code

const response = await fetch(`${baseUrl}/api/v1/roles`, {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${corporationToken}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'Finance manager',
    permissions: ['TransactionCreate', 'TransactionManage']
  })
});
const role = await response.json();
response = requests.post(
    f"{base_url}/api/v1/roles",
    headers={
        "Authorization": f"Bearer {corporation_token}",
        "Content-Type": "application/json",
    },
    json={
        "name": "Finance manager",
        "permissions": ["TransactionCreate", "TransactionManage"],
    },
)
role = response.json()
body, _ := json.Marshal(map[string]interface{}{
    "name":        "Finance manager",
    "permissions": []string{"TransactionCreate", "TransactionManage"},
})

req, _ := http.NewRequest("POST", baseURL+"/api/v1/roles", bytes.NewBuffer(body))
req.Header.Set("Authorization", "Bearer "+corporationToken)
req.Header.Set("Content-Type", "application/json")

resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()

var role RoleResponse
json.NewDecoder(resp.Body).Decode(&role)

Update a Custom Role

PUT /api/v1/roles/{roleId}

Requires EmployeeSu.

Request body:

{
  "name": "Finance manager",
  "permissions": ["TransactionCreate", "TransactionManage", "TransactionView"]
}
FieldTypeRequiredDescription
namestringYesDisplay name
permissionsarray of stringYesThe complete permission set. This replaces the previous set, it does not add to it

The response shape matches POST /api/v1/roles.

Employees holding this role keep their old permissions until they log in again. The permission
set is resolved at login and frozen into the corporation token. Widening a role is therefore delayed,
and narrowing a role is not a revocation — plan around the token lifetime.


Delete a Custom Role

DELETE /api/v1/roles/{roleId}

Requires EmployeeSu.

Response:

{}

A default role is refused with 400 and Default roles cannot be deleted.

Move employees off a role before deleting it. The API does not reassign them, and an employee whose
role no longer resolves cannot obtain a usable corporation token.


Error Handling

{
  "error_reason": "ErrorGeneral",
  "error_description": "Request failed validation",
  "error_category": {
    "category": "CategoryValidationFailure",
    "http_status_code": 400
  },
  "error_details": [
    { "key": "permissions", "details": "Su permission is not allowed for custom roles" }
  ]
}

Validation Errors (400)

Error Details KeyDescriptionResolution
nameRole name is requiredSend a non-empty name
nameRole name must be less than or equal to 64 bytesShorten the name
permissionsAt least one permission is requiredSend at least one entry
permissionsPermission name must not be emptyRemove blank entries
permissionsUnknown permission: <value>Use an exact name from the list above
permissionsSu permission is not allowed for custom rolesRemove Su
role_idDefault roles cannot be deletedOnly custom roles are deletable

Permission Errors (403)

Error ReasonDescriptionResolution
ErrorPermissionDeniedUser does not have required permissionsRole writes require EmployeeSu; role reads require EmployeeView or higher

Server Errors (500)

Error ReasonDescriptionResolution
ErrorGeneralFailed to get roleThe role id does not exist for this corporation
ErrorGeneralFailed to create roleDownstream rejection — most often a duplicate role name
ErrorGeneralFailed to delete roleDownstream rejection, typically because the role is still in use

Did this page help you?