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.
Deprecated. The Role Bindings API is superseded by the Grants API. It keeps working, but every answer carries Deprecation: true and a Link header naming /api/grants as its successor. New integrations should use grants; see Migrating to grants.

Intro

A role binding is one sentence: this principal has this role, here. It is how every kind of access in LangWatch is expressed, so this is the API to read when you want to know who can reach what. Each binding names exactly one principal, and there are three kinds:

Authentication

Requires an organization-level API key with organization:manage. Pass it as a Bearer token:

Endpoints

GET filters on userId, groupId, apiKeyId, scopeType and scopeId, and pages with offset and limit. limit defaults to 50 and cannot exceed 200. totalCount counts the filtered set, not the organization.

Scopes

The roles are ADMIN, MEMBER, VIEWER, and CUSTOM. CUSTOM carries a customRoleId from the Roles API, and is refused without one (custom_role_id_required). Some permissions only mean something for the organization as a whole, membership and billing among them. Granting one through a team- or project-scoped binding is refused at write time with org_exclusive_permission_scope, rather than being accepted and quietly having no effect.

Creating twice makes two bindings

Each create makes its own binding with its own id, even when the same principal already holds the same role at the same scope. A provisioning tool that must not duplicate should list first, or move to the Grants API, whose create takes an Idempotency-Key. A PATCH changes the role; the principal and the scope stay as they are, so moving a grant somewhere else means a new binding. You cannot bind a role carrying a permission you do not hold yourself at that scope, and a request made with an API key is judged by the key’s own permissions. The refusal is 403 grant_exceeds_caller_permissions, with the permissions you lack in meta.missingPermissions. See Roles & access.

hasLegacyAccessNotice

A create response can carry hasLegacyAccessNotice: true. It means this is the person’s first explicit binding, and until now their access came from plain team membership, which this write switches off. No call failed and no call is blocked: it is there so a tool can tell an operator that this user’s access is now described entirely by bindings.

Errors

Migrating to grants

Grants and role bindings are the same records, so existing bindings appear in GET /api/v1/grants straight away and no copying is needed. Change the calls: Grants also add an optional expiresAt, a status filter, and an Idempotency-Key on create.
Last modified on October 12, 2026