Skip to main content
The AI Gateway is governed by LangWatch’s existing RBAC system, the same resources-and-actions model you already use for traces, datasets, evaluations, and prompts. Five gateway-specific resources cover the gateway surface; provider credentials reuse the platform-wide modelProviders resource (no separate gatewayProviders resource since the binding model was retired). Permissions are checked scope-aware: every guard names the scope (ORGANIZATION, TEAM, PROJECT) the action operates on, and a higher-scope grant cascades downward (org manage allows team and project actions).
This page documents the default permission matrix. Custom roles can carry any combination of the permissions below, see Custom Roles for how to define one.

Resources

Every gateway UI, tRPC call, and REST endpoint checks one of these six resource keys:
ResourceWhat it gates
virtualKeysVK list, detail, drawer, create, update, rotate, revoke, archive. Plus viewOtherPersonal for org-admin off-boarding sweeps over other users’ personal VKs.
gatewayBudgetsBudget list, detail, drawer, create, update, archive, spend view.
modelProvidersModelProvider list, create, update, basic credentials, AND the Advanced (Gateway) tab (RPM, TPM, RPD, fallback priority, providerConfig). Same resource the platform already uses; no gateway-specific provider resource exists.
gatewayGuardrailsAttaching, detaching guardrails to a VK (the evaluator itself is still evaluations scope).
gatewayLogsAudit log list, filter, export.
gatewayUsage/gateway/usage stat tiles + time-windowed spend aggregation.

Actions

Each resource supports a subset of the standard CRUD action verbs:
ActionSemantics
viewRead, list + detail pages + REST GET endpoints.
createMint a new record (VK, budget).
updateEdit an existing record’s fields (but not delete or rotate).
rotateVK-specific: issue a fresh secret and retire the old one with a 24 h grace window.
deleteSoft-delete, archive. Budgets use archived = true; VKs use revoked = true. Hard-delete is not a gateway operation.
attach, detachGuardrail-specific: link, unlink an evaluator to a VK direction (pre, post, stream_chunk).
viewOtherPersonalVK-specific: view personal VKs whose principalUserId is not the calling user. Used by org admins for off-boarding sweeps. Without this perm, api.personalVirtualKeys.list always filters to the caller’s own personal VKs.
manageSuperset, implies all CRUD actions for the resource.
The full permission set uses the <resource>:<action> format, for example virtualKeys:rotate, gatewayBudgets:manage, gatewayGuardrails:attach, virtualKeys:viewOtherPersonal. Custom roles can pick any subset.

Multi-scope intersection

When a VK is created with multiple scope rows (e.g. TEAM:platform AND TEAM:data-sci), the caller must hold virtualKeys:manage at every named scope. Holding it at only one does not pass; the error names the unauthorised scope. Holding virtualKeys:manage at ORGANIZATION cascades down to every TEAM and PROJECT in the org in a single grant.

Default role matrix

PermissionADMINMEMBERVIEWER
virtualKeys:view
virtualKeys:create
virtualKeys:update
virtualKeys:rotate
virtualKeys:delete
virtualKeys:manage
virtualKeys:viewOtherPersonal
gatewayBudgets:view
gatewayBudgets:create
gatewayBudgets:update
gatewayBudgets:delete
gatewayBudgets:manage
modelProviders:view
modelProviders:update
modelProviders:manage
gatewayGuardrails:view
gatewayGuardrails:attach
gatewayGuardrails:detach
gatewayGuardrails:manage
gatewayLogs:view
gatewayUsage:view

What each default role can actually do

ADMIN: full gateway control. Can mint VKs at any scope, audit other users’ personal VKs (viewOtherPersonal), set budgets (and delete them), edit ModelProvider Advanced (Gateway) settings, attach/detach guardrails, read audit logs, view usage. MEMBER: can mint + rotate their own VKs, view their own personal VKs, read budgets + providers + guardrails, read audit logs, view usage. Cannot create or delete budgets (finance-level control lives with admins), cannot edit ModelProvider Advanced settings, cannot attach/detach guardrails (policy change belongs to admins), and cannot view other users’ personal VKs. VIEWER: read-only across every gateway surface. Can see VKs (but cannot see the secret, the secret is only ever shown at create-time, regardless of role), budgets, ModelProvider basic + Advanced fields (read-only), guardrail attachments, audit logs, usage. Cannot see other users’ personal VKs. CUSTOM: inherits no gateway permissions by default. A custom role starts empty and must be granted the specific permissions you want.

Public REST API scoping

Every /api/gateway/v1/* endpoint enforces the same permission as its equivalent UI action. The mapping is:
EndpointPermission
GET /api/gateway/v1/virtual-keysvirtualKeys:view
POST /api/gateway/v1/virtual-keysvirtualKeys:create
PATCH /api/gateway/v1/virtual-keys/:idvirtualKeys:update
POST /api/gateway/v1/virtual-keys/:id/rotatevirtualKeys:rotate
POST /api/gateway/v1/virtual-keys/:id/revokevirtualKeys:delete
GET /api/gateway/v1/budgetsgatewayBudgets:view
POST /api/gateway/v1/budgetsgatewayBudgets:create
PATCH /api/gateway/v1/budgets/:idgatewayBudgets:update
POST /api/gateway/v1/budgets/:id/archivegatewayBudgets:delete
POST /api/gateway/v1/virtual-keys/:id/guardrailsgatewayGuardrails:attach
DELETE /api/gateway/v1/virtual-keys/:id/guardrails/:guardrailIdgatewayGuardrails:detach
GET /api/gateway/v1/usagegatewayUsage:view
Audit log retrieval is not exposed via the public REST API in v1, gateway audit rows live in the platform AuditLog table and are read via /settings/audit-log (CSV export) or via the tRPC organization.getAuditLogs procedure (gated on auditLog:view, with TeamUser ADMIN/MEMBER/VIEWER fallback for legacy admins). See Audit log → Querying programmatically. The gatewayLogs:view permission remains in the enum for backward compatibility but is no longer wired to any endpoint. A request with an API token that lacks the required permission returns 403 permission_denied with the missing permission named in the error.message:
{
  "error": {
    "type":    "permission_denied",
    "code":    "permission_denied",
    "message": "missing permission: virtualKeys:create",
    "param":   null
  }
}

Personal VKs (principal-attributed)

The default role matrix governs who can mint a VK: a VK can also be marked as personal by setting principalUserId on the record. The principal column is orthogonal to the VK’s scope rows.
  • A VK with principalUserId = user_abc is treated as that user’s personal credential. Spend cascades through the principal’s PRINCIPAL-scope budget first, then through scope-driven budgets (TEAM, ORGANIZATION).
  • A VK with principalUserId = null is shared (service-account-style): spend cascades through scope-driven budgets only.
  • The CLI device-flow (langwatch login --device) lazy-mints a personal VK at ORGANIZATION scope with the caller as the principal. No explicit virtualKeys:manage grant is required for this self-mint path.
  • Visibility: a user always sees their own personal VKs without any explicit grant (principal-match short-circuit). Viewing other users’ personal VKs requires virtualKeys:viewOtherPersonal.
For full semantics see vk-personal-scope.feature.

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 LangWatch UI (SCIM doesn’t carry custom-role identifiers); the SCIM integration only maps to ADMIN, MEMBER, VIEWER.

Changing the permission matrix

Default roles are code-defined in langwatch/src/server/api/rbac.ts, see commit history for the shipping order. Do not edit the default matrix in production: changes to TEAM_ROLE_PERMISSIONS affect 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. For the policy rationale (why MEMBER can mint VKs but not create budgets, etc.), see spec contract §10, Permissions.

See also