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:
| Guide | Why |
|---|---|
| Getting Started | Platform overview and setup |
| Api Basics | Required headers and request configuration |
| Permissions | What each permission grants |
| Employees | A 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 roles | Custom roles | |
|---|---|---|
is_global | true | false |
| Scope | Every corporation on the platform | One corporation |
| Source | Seeded in the CorporateAccounts contract | Created through the API |
| Editable | No | Yes |
| Deletable | No | Yes |
May grant Su | Yes — Owner does | No |
Default Roles
| Role | Role id | Permissions |
|---|---|---|
| Owner | ed7a5845-8727-4b06-9ba0-7d43ddc9e6aa | Su |
| Admin | 31eba1db-0809-4d44-a479-0b696c4a2603 | CardSu, EmployeeSu, TransactionSu, AccountSu |
| Treasury | ce3de0cc-bd42-4bc8-829c-e395fab506ef | TransactionSu, CardSu, AccountSu |
| HR | a1b2c3d4-e5f6-0718-29a0-b1c2d3e4f506 | EmployeeSu |
| Member | 7386b48f-df9c-49fe-8ccc-38b5b0b55932 | TransactionCreate, TransactionView, CardView, AccountView, EmployeeView |
| Viewer | 240d596c-8973-4203-827f-a2c994bceafa | TransactionView, 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.
AdminandTreasurydiffer by one permission —EmployeeSu.Treasurycan 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:
| Parameter | Description |
|---|---|
page_number | 1-indexed page number |
page_size | Page size. Defaults to 25 |
sort | name (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"
}
]
}| Field | Description |
|---|---|
id | Role id. The value passed as role_id when inviting an employee or changing their role |
name | Display name |
permissions | Permission names granted by this role |
is_global | true for a default role, false for a custom role |
created_at / updated_at | ISO 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:
| Parameter | Description |
|---|---|
{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"]
}| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name. Trimmed, must be non-empty and at most 64 bytes |
permissions | array of string | Yes | At 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
Suis recognised but refused for custom roles. Including it returns400with
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.
transactioncreateis
rejected withUnknown permission: transactioncreate. Surrounding whitespace is trimmed; an entry
that is only whitespace is rejected withPermission 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"]
}| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name |
permissions | array of string | Yes | The 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 Key | Description | Resolution |
|---|---|---|
name | Role name is required | Send a non-empty name |
name | Role name must be less than or equal to 64 bytes | Shorten the name |
permissions | At least one permission is required | Send at least one entry |
permissions | Permission name must not be empty | Remove blank entries |
permissions | Unknown permission: <value> | Use an exact name from the list above |
permissions | Su permission is not allowed for custom roles | Remove Su |
role_id | Default roles cannot be deleted | Only custom roles are deletable |
Permission Errors (403)
| Error Reason | Description | Resolution |
|---|---|---|
ErrorPermissionDenied | User does not have required permissions | Role writes require EmployeeSu; role reads require EmployeeView or higher |
Server Errors (500)
| Error Reason | Description | Resolution |
|---|---|---|
ErrorGeneral | Failed to get role | The role id does not exist for this corporation |
ErrorGeneral | Failed to create role | Downstream rejection — most often a duplicate role name |
ErrorGeneral | Failed to delete role | Downstream rejection, typically because the role is still in use |
Updated 20 days ago

