Skip to main content

How gateway permissions work

The LangWatch AI Gateway uses the LangWatch permission model: a permission is <resource>:<action>, a role is a set of permissions, and a role binding gives a person a role on an organization, a team or a project. A grant at a higher scope covers the scopes below it: virtualKeys:manage on the organization covers every team and project in it. The gateway adds its own resources to that model. Provider credentials, the audit log and the webhook endpoints are shared with the rest of LangWatch. See Access Control (RBAC) for the model and role bindings.
The matrix below is the default. Custom roles can carry any combination of the permissions listed, see Custom Roles for how to define one.

Resources

gatewaySpend and webhookEndpoints come only from an organization role binding. A team or project binding never grants them. virtualKeys:viewOtherPersonal lets an administrator see other people’s personal keys. Everyone sees their own personal keys without it.

What the built-in roles hold

Team roles apply on the teams and projects the binding names. Organization roles apply on the whole organization. The Organization Admin column lists what the organization role grants on its own. An organization admin gets the team permissions through the team role bindings they hold, like everyone else. Organization Member and External hold no gateway permission on their own. They get gateway access through a team role binding.

Creating keys at a scope

Creating a key with an organization, team or project scope needs virtualKeys:manage at every scope the key names. The same rule applies when you re-scope a key with PATCH. Naming a trace project on a team or organization key needs virtualKeys:manage on that project. A personal key needs only membership of the organization. See Virtual keys.

Permission per REST route

A call with an API key that lacks the route’s permission gets HTTP 403 api_key_permission_denied, with the permission in error.meta.permission. A call that holds the route’s permission but not virtualKeys:manage on a scope the key names gets HTTP 403 permission_denied with the scope in the message.
Also check: Access Control (RBAC) for role bindings, Virtual keys for scopes, and Billing and spend events for the spend routes.

Changing the permission matrix

Default roles are code-defined in modules/authz/contract/src/roles.ts, declared as differences rather than duplicate lists: viewer is the base set, member is viewer plus its additions, admin is member plus its additions, and builtinRolePermissions() resolves a role key to the permissions it ends up with. Do not edit the default matrix in production: a change to those built-in sets affects every team on the instance. Use a custom role instead: create it in the UI, assign the permissions you want, and assign it to specific users.

SCIM + SSO

Default role matrix assignments flow through SCIM: when an IdP pushes a user with role MEMBER, they pick up the gateway permissions in the MEMBER column above. Custom roles must be assigned via the LangWatch UI (SCIM doesn’t carry custom-role identifiers); the SCIM integration only maps to ADMIN, MEMBER, VIEWER.
Last modified on October 12, 2026