Available on Enterprise plans. An organization without an Enterprise plan is refused with HTTP
402 and the error code
enterprise_plan_required. Self-hosted deployments need an Enterprise
license for the same endpoints. To enable it, reach out to
enterprise@langwatch.ai.Intro
The Roles API manages custom roles: named permission sets your organization defines, for when the built-in roles are not the shape you need. It can also list the built-in roles, addressed by the stable idsadmin, member and viewer.
A role on its own grants no access. It becomes access when a grant hands it to a user, a group or an API key at a scope. Roles are what, grants are who and where. See Roles & access.
Authentication
Requires an organization-level API key withorganization:manage. Pass it as a Bearer token:
Endpoints
Permissions are resource:action keys
Start at the catalog. It lists every resource with the actions it takes, and flags the resources that only mean anything at organization scope, which is worth knowing before you build a role a team-scoped binding can never fully grant.
Built-in roles
GET /api/v1/roles lists only the custom roles. ?builtIn=true lists only the built-in roles admin, member and viewer (marked "builtIn": true, with createdAt and updatedAt null) and ?builtIn=false only the custom ones; any other value is refused with 422. GET /api/v1/roles/admin reads a built-in role and its permissions. A built-in role cannot be changed or deleted: 409 role_is_built_in.
You cannot add a permission you do not hold
Creating a role, or adding permissions to one, is refused with 403role_exceeds_caller_permissions when you do not hold every added permission yourself; meta.missingPermissions lists them. A request made with an API key is judged by the key’s own permissions. Permissions a role already carries are not held against you, so renaming it still works.
Names are the natural key
A role name is unique inside the organization, which makes it the handle an infrastructure-as-code run can rely on. Reusing a name is refused with 409custom_role_name_taken rather than quietly creating a second role that looks identical in every list.
Names beginning with apikey: are reserved for LangWatch’s own use and are refused with 422 custom_role_name_reserved.
Updating replaces the permission set
PATCH is partial at the field level: send name alone and only the name changes. A permissions list, when you send one, replaces the whole set rather than adding to it, so what you send is what the role has afterward. That is what lets a declarative tool converge without reading first.
Deleting a role something still holds
A role that a role binding or a team assignment still points at cannot be deleted. The refusal is 409custom_role_in_use, with the counts in meta so you know how much is holding it:
bindingCount counts role bindings, userCount counts direct team assignments. Remove or re-point those first, then delete the role.