put_roles_by_id
Calls PUT /api/roles/{id} as the signed-in person · REST reference
Update a role
Requires execute:UpdateRoleCommand; platform staff bypass that gate. The caller's own token is forwarded to the DAL, which requires execute:UpdateRoleCommand in the role's own organization and refuses — 403 — a system role, a role of the platform organization, or a role that belongs to no organization to anyone without platform authority. For such a caller, a role in an organization where they do not hold admin.roles.view is a 404. A SYSTEM ROLE IS NOT UPDATABLE: a rename is refused with 400 SYSTEM_ROLE_PROTECTION before the DAL is called, and any other update is refused by the DAL — 403 without platform authority, 409 with it. THE BODY IS NOT ALLOW-LISTED. BaseController.update copies EVERY key of the request body onto the model except id, createdAt and createdBy, so status, groupCount, metadata and isSystem are all assignable even though only name, description, status, metadata and the permission deltas reach the DAL — the rest are echoed in the 200 without being stored. permissions is treated as the DESIRED FULL SET: the model diffs it against the stored permissions and sends add/remove deltas, so omitting a currently-held permission REVOKES it. Omitting the field entirely changes nothing. Permission names are stored only on a role that belongs to no organization: for an organization's role, whose grants are :OrgPermission edges, the DAL refuses any change to them and the answer is 400.
Arguments
| Argument | Type | Description | |
|---|---|---|---|
idpath | string (uuid) | required | |
bodyJSON | object | required | The request body, as a JSON object. Its fields are listed below. |
Fields of body
| Field | Type | Description | |
|---|---|---|---|
description | string | ||
name | string | Rejected with 400 when the target role is a system role. | |
permissions | array of string | The complete desired set. Absent members are revoked. | |
status | "ACTIVE" | "INACTIVE" | "ARCHIVED" | Not validated by the schema, but forwarded to the DAL. |
Example call
A client sends this over the MCP session; Claude and other agents build it for you. The values are placeholders — the shape is real.
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "put_roles_by_id",
"arguments": {
"id": "11111111-1111-4111-8111-111111111111",
"body": {
"description": "string",
"name": "string",
"permissions": [
"string"
],
"status": "ACTIVE"
}
}
}
}Result
On success (HTTP 200) the tool returns one text item holding the API's JSON, pretty-printed.
The mutated Role model, echoed before the DAL entity is read back. ISO-8601 timestamps.
{
"data": {
"createdAt": "2026-01-15T12:00:00Z",
"groupCount": 1,
"id": "11111111-1111-4111-8111-111111111111",
"isSystem": true,
"metadata": {},
"name": "string",
"permissions": [
"string"
],
"status": "ACTIVE",
"description": "string",
"updatedAt": "2026-01-15T12:00:00Z"
},
"message": "Role updated successfully",
"success": true
}Fields
| Field | Type | Description |
|---|---|---|
data | object | |
data.createdAt | string (date-time) | |
data.description | string | null | |
data.groupCount | integer | |
data.id | string (uuid) | |
data.isSystem | boolean | |
data.metadata | object | |
data.name | string | |
data.permissions | array of string | |
data.status | "ACTIVE" | "INACTIVE" | "ARCHIVED" | |
data.updatedAt | string (date-time) | null | |
message | "Role updated successfully" | |
success | true |
When it fails
A failed call returns isError: true with the API's message as text. A change the person declines, or lets time out, also comes back this way and nothing changes.
400express-validator rejection (bad
id, oversizedname/description, malformedpermissions) with an ARRAYdetails; the system-role rename guard withcode: SYSTEM_ROLE_PROTECTION; or the DAL's own validation refusing the update — a name that is not 2-50 letters, digits and underscores, a permission name not of theexecute:/query:shape, or any permission change on an organization's role — carrying the DAL's sentence.403Authenticated, but the caller holds none of the required permissions.
404No such role —
Role not foundwithcode: ROLE_NOT_FOUND— or a DAL NOT_FOUND refusal, which carries the DAL sentence and no code.409The resource is not in a state that allows this operation.
What the model is shown
This is the tool exactly as the server lists it in tools/list. The description starts with the REST call it makes.
{
"name": "put_roles_by_id",
"description": "PUT /api/roles/{id} — Requires `execute:UpdateRoleCommand`; platform staff bypass that gate. The caller's own token is forwarded to the DAL, which requires `execute:UpdateRoleCommand` in the role's own organization and refuses — 403 — a system role, a role of the platform organization, or a role that belongs to no organization to anyone without platform authority. For such a caller, a role in an organization where they do not hold `admin.roles.view` is a 404. A SYSTEM ROLE IS NOT UPDATABLE: a rename is refused with 400 `SYSTEM_ROLE_PROTECTION` before the DAL is called, and any other update is refused by the DAL — 403 without platform authority, 409 with it. THE BODY IS NOT ALLOW-LISTED. `BaseController.update` copies EVERY key of the request body onto the model except `id`, `createdAt` and `createdBy`, so `status`, `groupCount`, `metadata` and `isSystem` are all assignable even though only `name`, `description`, `status`, `metadata` and the permission deltas reach the DAL — the rest are echoed in the 200 without being stored. `permissions` is treated as the DESIRED FULL SET: the model diffs it against the stored permissions and sends add/remove deltas, so omitting a currently-held permission REVOKES it. Omitting the field entirely changes nothing. Permission names are stored only on a role that belongs to no organization: for an organization's role, whose grants are `:OrgPermission` edges, the DAL refuses any change to them and the answer is 400.\n\nThis changes data. The person is asked to confirm it before it runs, and nothing happens if they decline.",
"inputSchema": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"body": {
"type": "string",
"description": "The JSON request body: { description: string; name: string (Rejected with 400 when the target role is a system role.); permissions: array of string (The complete desired set.); status: one of \"ACTIVE\" | \"INACTIVE\" | \"ARCHIVED\" (Not validated by the schema, but forwarded to the DAL.) }"
}
},
"required": [
"id",
"body"
]
},
"annotations": {
"readOnlyHint": false,
"destructiveHint": true,
"idempotentHint": true,
"openWorldHint": false
}
}