Skip to main content
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 ids admin, 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 with organization: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.
Then create a role from the keys it gave you:

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 403 role_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 409 custom_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 409 custom_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.

Errors

Last modified on October 13, 2026