6.9 KiB
Teams & Access Control
My Teams
GET /teams/mine → { "data": [...] }
Auth: Authenticated user.
Returns teams the current user is a member of (active teams only).
Each team object:
{
"id": "uuid",
"name": "Engineering",
"description": "...",
"is_active": true,
"settings": "{}",
"my_role": "admin|member",
"member_count": 5
}
Team Administration
Team CRUD (platform admin):
GET /admin/teams → { "data": [...], "total": N, "page": N, "per_page": N }
POST /admin/teams ← { "name", "description" } → { "id", "name" }
GET /admin/teams/:id → team object (unwrapped)
PUT /admin/teams/:id ← { "name"?, "description"?, "is_active"?, "settings"? }
DELETE /admin/teams/:id
Auth: RequireAdmin (platform admin).
POST returns 201. PUT/DELETE return {"ok": true}. PUT with
no fields returns 400. Duplicate name returns 409.
Members (admin routes):
GET /admin/teams/:id/members → { "data": [...] }
POST /admin/teams/:id/members ← { "user_id", "role" } → { "id" }
PUT /admin/teams/:id/members/:memberId ← { "role" }
DELETE /admin/teams/:id/members/:memberId
Auth: RequireAdmin.
role must be admin or member. Duplicate membership returns 409.
Team-scoped routes (team admin self-service):
GET /teams/:teamId/members
POST /teams/:teamId/members ← { "user_id", "role" }
PUT /teams/:teamId/members/:memberId
DELETE /teams/:teamId/members/:memberId
Auth: RequireTeamAdmin.
Same handlers as admin routes; the getTeamID() helper reads either
:id or :teamId.
Team Providers:
GET /teams/:teamId/providers → { "data": [...], "allow_team_providers": bool }
POST /teams/:teamId/providers ← { "name", "provider", "endpoint", "api_key", ... }
PUT /teams/:teamId/providers/:id
DELETE /teams/:teamId/providers/:id
GET /teams/:teamId/providers/:id/models → { "models": [...], "provider": "name" }
Auth: RequireTeamAdmin.
GET returns provider configs scoped to the team. The models endpoint
performs a live query to the upstream provider.
Team Models:
GET /teams/:teamId/models → { "models": [...] }
Auth: RequireTeamAdmin.
Returns all models available to this team: global admin models with
visibility IN ('enabled', 'team') plus live-queried team provider
models with source field ("global" or "team").
Team Groups:
GET /teams/:teamId/groups → { "data": [...] }
Auth: RequireTeamAdmin.
Returns groups scoped to this team.
Team Personas: See personas.md §Team Personas.
Team Roles:
GET /teams/:teamId/roles → { "data": {...} }
PUT /teams/:teamId/roles/:role ← RoleConfig object
DELETE /teams/:teamId/roles/:role
Auth: RequireTeamAdmin.
GET returns the team's model_roles settings map (composite object,
not an array). Initially empty {}. Each key is a role name, value is
a RoleConfig. PUT validates the role name. DELETE removes the
override, falling back to the global role definition.
Team Audit:
GET /teams/:teamId/audit → { "data": [...], "total": N, "page": N, "per_page": N }
GET /teams/:teamId/audit/actions → { "actions": [...] }
Auth: RequireTeamAdmin.
Audit log is scoped to actions performed by team members. Supports
query filters: ?action=, ?actor_id=, ?resource_type=.
Team Usage:
GET /teams/:teamId/usage → { "totals": {...}, "results": [...] }
Auth: RequireTeamAdmin.
Returns usage against team-owned providers. Supports query params for date range filtering.
Groups & Resource Grants
Groups are ACL containers that decouple access from team membership. A resource grant controls who can see a Persona or KB beyond its base scope.
My Groups:
GET /groups/mine → { "data": [...] }
Auth: Authenticated user.
Admin Group CRUD:
GET /admin/groups → { "data": [...] }
POST /admin/groups ← { "name", "scope", "team_id"? }
GET /admin/groups/:id → group object (unwrapped)
PUT /admin/groups/:id ← { "name"?, "description"?, "permissions"?, ... }
DELETE /admin/groups/:id
Auth: RequireAdmin.
POST returns 201. Duplicate name returns 409. Deleting a
system-sourced group (e.g. Everyone) returns 400.
Group Members:
GET /admin/groups/:id/members → { "data": [...] }
POST /admin/groups/:id/members ← { "user_id" }
DELETE /admin/groups/:id/members/:userId
Auth: RequireAdmin.
Resource Grants:
GET /admin/grants/:type/:id → grant object (unwrapped)
PUT /admin/grants/:type/:id ← { "grant_scope", "granted_groups"?: [...] }
DELETE /admin/grants/:type/:id
Auth: RequireAdmin.
:type is persona or kb. :id is the resource ID.
Grant scopes:
grant_scope |
Visibility |
|---|---|
team_only |
Only the owning team |
global |
All authenticated users |
groups |
Members of specified groups |
Setting grant_scope: "groups" without granted_groups returns 400.
Permissions
Groups carry fine-grained permissions. Permission resolution: union of all group permissions + Everyone group permissions.
List all permissions:
GET /admin/permissions → { "permissions": ["model.use", "kb.read", ...] }
Auth: RequireAdmin.
Get user's effective permissions:
GET /admin/users/:id/permissions → { "permissions": [...], "user_id": "...", "groups": [...] }
Auth: RequireAdmin.
Returns the resolved permission set and contributing group IDs (always includes the Everyone group).
Group permission fields (set via PUT /admin/groups/:id):
{
"permissions": ["model.use", "kb.read", "channel.create"],
"token_budget_daily": 100000,
"token_budget_monthly": 3000000,
"allowed_models": ["claude-sonnet-4-20250514", "gpt-4o"]
}
| Field | Type | Description |
|---|---|---|
permissions |
string[] | Permission constants granted to members |
token_budget_daily |
int/null | Daily token ceiling (NULL = unlimited) |
token_budget_monthly |
int/null | Monthly token ceiling (NULL = unlimited) |
allowed_models |
string[]/null | Model allowlist (NULL = unrestricted) |
Everyone group: System-seeded group (ID 00000000-0000-0000-0000-000000000001)
whose permissions apply to all authenticated users implicitly (no membership
row needed). Editable by admins, cannot be deleted (source=system).
RequirePermission middleware: Routes gated by permission check user's resolved permission set. Returns 403 if the required permission is not present.
See enums.md for the full permission constant list.