def create(
body: RoleCreate,
*,
timeout: float | None = None,
) -> RoleResourceasync def create(
body: RoleCreate,
*,
timeout: float | None = None,
) -> RoleResourceCreates a custom role from a full permission list. Take the strings from list_permissions, because anything outside that catalogue is rejected with 400 request.body_invalid.
Two separate ceilings apply to body['permissions']. Permissions flagged 'assignableToCustomRoles': False, today workspace:delete and billing:manage, can never be held by a custom role and fail validation for everyone. Beyond that you can only grant what you already hold: the request is checked against the effective permissions of the user who owns the API key, and anything past that fails with 403 auth.permission_denied naming the offending strings in message. An admin therefore cannot mint a role stronger than admin.
Names are trimmed and must be unique within the workspace case insensitively, so Deploy bot collides with deploy bot and returns 409 role.name_conflict. A workspace holds at most 25 custom roles, and the cap is checked before the name, so a full workspace fails with 409 role.limit_reached even when the name was also taken.
Example
from envless import envless
catalogue = envless.roles.list_permissions()
candidates = [
*catalogue['byResource'].get('variables', []),
*catalogue['byResource'].get('versions', []),
]
role = envless.roles.create(
{
'name': 'Deploy bot',
'description': 'Read only access for CI',
'permissions': [
name
for name in candidates
if name.endswith(':read')
],
}
)
print(role['id'], role['permissions'])import asyncio
from envless import AsyncEnvless
async def main() -> None:
async with AsyncEnvless() as envless:
catalogue = await envless.roles.list_permissions()
candidates = [
*catalogue['byResource'].get('variables', []),
*catalogue['byResource'].get('versions', []),
]
role = await envless.roles.create(
{
'name': 'Deploy bot',
'description': 'Read only access for CI',
'permissions': [
name
for name in candidates
if name.endswith(':read')
],
}
)
print(role['id'], role['permissions'])
asyncio.run(main())Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
body['name'] | str | Yes | Display name for the role, trimmed and unique within the workspace. |
body['permissions'] | list[str] | Yes | Complete list of permission strings from the catalogue, with no merge or inheritance applied. |
body['description'] | str | None | No | Optional free text shown next to the role. |
timeout | float | No | Seconds each attempt of this call may take, the response body included, before it raises EnvlessNetworkError with is_timeout set. Overrides the client's timeout for this call, and 0 turns the limit off. |
Returns
RoleResource for the new role, with isSystem False and systemKey None.
Worth knowing
- System roles do not count against the 25 role cap, only custom ones do.
- Creating a role emits the
role.createdwebhook event with the permission list in the payload. - Use
catalogue['systemRoles']['admin']as a template when you want a slightly reduced admin, then remove what the role should not have.
Required scopes
roles:write
Underlying endpoint
POST /roles
Request and response schema, every error code, and a live playground.
In TypeScript
envless.roles.create()
The same method in @goenvless/sdk, with the same request, retries and errors.