ClouisleClouisle

Teams API

管理团队信息、成员角色、所有权移交与团队模型授权

Teams API 覆盖三类接口:平台侧团队与成员管理(/api/v1/teams)、管理后台团队管理(/api/v1/admin/teams,创建与删除只在这里),以及团队模型授权(/api/v1/teams/{team_id}/models*,仅超级管理员可写)。

没有平台侧创建/删除团队接口

平台路由只有 GET /api/v1/teams/my、GET|PUT /api/v1/teams/{team_id}、成员管理、leave 与 transfer-ownership。没有 POST /api/v1/teams,也没有 DELETE /api/v1/teams/{team_id};创建与删除团队属于管理后台,分别在 POST /api/v1/admin/teams 与 DELETE /api/v1/admin/teams/{team_id}。

角色与权限模型

角色说明
owner团队所有者,每个团队一个。可改成员角色、移交所有权、更新团队;不能直接退出,必须先移交
admin团队管理员。可更新团队、增删成员(改为 admin 需要被操作者已具备 team:manage 全局权限)
member普通成员。默认角色,按全局权限执行操作
viewer只读成员。仅允许读取类权限(team:read、agent:read、workflow:read、kb:read、tool:read、conversation:read 等);任何写操作都会被拒绝

权限码与操作的关系:

权限码覆盖的操作
team:read查看团队详情、查看成员、退出团队(自我移除)
team:update更新团队名称、描述、头像
team:manage添加/移除成员、修改成员角色、移交所有权
admin:team:read后台列出全部团队
admin:team:create后台创建团队
admin:team:delete后台删除团队

校验顺序(check_team_permission)对每个平台端点一致:

  1. 团队存在,否则 404 / 4004。
  2. 超级管理员直接通过,跳过后续检查。
  3. 必须是团队成员,否则 403 / 3002。
  4. 需要管理动作时(team:update、team:manage 或端点显式要求),角色必须是 owner 或 admin,否则 403 / 3003。
  5. viewer 角色只能执行只读权限,否则 403 / 3000。
  6. 最后校验用户的全局权限码,缺失则 403 / 3000。

第 3 步也意味着「非本团队成员」看不到团队详情:即使团队对管理员可见,普通用户仍需先成为成员。全员可见的团队列表请用后台接口。

基础 URL 与认证

  • 平台接口:/api/v1/teams
  • 管理接口:/api/v1/admin/teams
  • 团队模型授权:/api/v1/teams/{team_id}/models*

所有团队端点都需要经过认证的 JWT 用户会话,不接受 API Key 认证。

端点总览

平台路由

方法路径用途权限 / 角色
GET/api/v1/teams/my列出当前用户所属团队及其角色登录即可
GET/api/v1/teams/{team_id}团队详情(含成员列表)team:read
PUT/api/v1/teams/{team_id}更新团队信息team:update + owner/admin
POST/api/v1/teams/{team_id}/members添加成员team:manage + owner/admin
PUT/api/v1/teams/{team_id}/members/{user_id}修改成员角色team:manage + owner/admin,且调用者为 owner(或超级管理员)
DELETE/api/v1/teams/{team_id}/members/{user_id}移除成员(支持自我移除)自我移除需 team:read;移除他人需 team:manage + owner/admin
POST/api/v1/teams/{team_id}/leave退出团队team:read
POST/api/v1/teams/{team_id}/transfer-ownership移交所有权team:manage + owner(或超级管理员)

管理后台路由

方法路径用途权限码
GET/api/v1/admin/teams分页列出全部团队admin:team:read
POST/api/v1/admin/teams创建团队admin:team:create
DELETE/api/v1/admin/teams/{team_id}删除团队admin:team:delete

后台接口没有 GET /admin/teams/{team_id} 与 PUT /admin/teams/{team_id}:查看与更新仍走平台路由(需要团队成员身份)。

列出我的团队

GET /api/v1/teams/my

不需要权限码,返回当前用户的全部成员关系,按 TeamMember 记录顺序:

curl -X GET "https://your-domain.com/api/v1/teams/my" \
  -H "Authorization: Bearer YOUR_TOKEN"

成功响应(200 OK):

{
  "code": 0,
  "data": [
    {
      "id": "8f14e45f-ceea-467a-9d0f-1a2b3c4d5e6f",
      "name": "Marketing Team",
      "description": "Marketing and content creation team",
      "avatar_url": null,
      "role": "member",
      "joined_at": "2026-01-15T10:00:00Z"
    }
  ],
  "msg": "success"
}

返回的是数组(不是分页信封)。没有 GET /api/v1/teams 列出全部团队;需要全量列表时使用后台接口:

GET /api/v1/admin/teams?page=1&page_size=50&search=marketing
参数类型默认值说明
pageinteger1页码
page_sizeinteger50每页条数(注意后台默认是 50,不是平台接口常见的 20)
searchstring—按团队名称或描述模糊匹配

获取团队详情

GET /api/v1/teams/{team_id}
路径参数类型必填
team_idstring (UUID)是

成功响应(200 OK):

{
  "code": 0,
  "data": {
    "id": "8f14e45f-ceea-467a-9d0f-1a2b3c4d5e6f",
    "name": "Marketing Team",
    "description": "Marketing and content creation team",
    "avatar_url": null,
    "is_default": false,
    "owner": {
      "id": "3f2a1b0c-9d8e-4f7a-8b6c-5d4e3f2a1b0c",
      "username": "alice",
      "email": "alice@example.com",
      "avatar_url": "https://example.com/avatars/alice.png"
    },
    "created_at": "2026-01-15T10:00:00Z",
    "updated_at": "2026-02-11T15:30:00Z",
    "members": [
      {
        "id": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e",
        "user_id": "3f2a1b0c-9d8e-4f7a-8b6c-5d4e3f2a1b0c",
        "username": "alice",
        "email": "alice@example.com",
        "avatar_url": "https://example.com/avatars/alice.png",
        "role": "owner",
        "joined_at": "2026-01-15T10:00:00Z"
      }
    ]
  },
  "msg": "success"
}

成员信息里没有独立的成员列表端点,完整成员列表只在这个响应中返回;members 未分页,成员很多的团队要自行截取。

更新团队

PUT /api/v1/teams/{team_id}

需要 team:update 权限码,并且调用者必须是团队 owner 或 admin。所有字段可选,只传要修改的字段。

字段类型说明
namestring团队名称,需全局唯一
descriptionstring团队描述
avatar_urlstring团队头像 URL
curl -X PUT "https://your-domain.com/api/v1/teams/8f14e45f-ceea-467a-9d0f-1a2b3c4d5e6f" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"name": "Growth Team"}'

响应返回更新后的团队对象(含 owner,不含 members)。重名返回 HTTP 400 / 5102(TEAM_NAME_EXISTS)。

只有显式传入的字段会被修改:传 "description": null 也会被当作「未提供」而跳过,因此当前无法用它清空描述或头像。

添加团队成员

POST /api/v1/teams/{team_id}/members

需要 team:manage 权限码,且调用者为 owner/admin。

字段类型必填默认值说明
user_idstring (UUID)二选一—目标用户 UUID
identifierstring二选一—用户名精确匹配,或邮箱(大小写不敏感)精确匹配
rolestring否member成员角色:member、admin、viewer

user_id 与 identifier 至少提供一个,都不提供返回 HTTP 400 / 1001。

curl -X POST "https://your-domain.com/api/v1/teams/8f14e45f-ceea-467a-9d0f-1a2b3c4d5e6f/members" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"identifier": "carol@example.com", "role": "member"}'

三条业务规则:

  1. 目标用户必须存在,否则 HTTP 404 / 4001。
  2. 目标用户不能已经是成员,否则 HTTP 400 / 5103(ALREADY_TEAM_MEMBER)。
  3. 不能直接添加 owner:返回 HTTP 400 / 5206(CANNOT_ADD_AS_OWNER),所有权只能通过移交接口改变。
  4. 添加为 admin 时,目标用户必须已经具备全局 team:manage 权限,否则 HTTP 403 / 3000(并回传 permission 字段说明缺哪个权限码)。

成功响应(200 OK): 返回新建的成员记录(id、user_id、username、email、avatar_url、role、joined_at)。同时会向被添加者与整个团队发送站内通知。

修改成员角色

PUT /api/v1/teams/{team_id}/members/{user_id}
路径参数类型必填
team_idstring (UUID)是
user_idstring (UUID)是

请求体:

{ "role": "admin" }

role 必填。约束:

  • 只有 owner(或超级管理员)能修改角色;admin 调用返回 HTTP 403 / 3004(TEAM_OWNER_REQUIRED)。
  • 不能修改 owner 自己的角色(HTTP 400 / 5207,CANNOT_CHANGE_OWNER_ROLE)。
  • 不能把成员直接提升为 owner(HTTP 400 / 5208,CANNOT_PROMOTE_TO_OWNER),请使用移交所有权。
  • 设为 admin 时,目标用户必须已有全局 team:manage 权限,否则 HTTP 403 / 3000。
  • 目标用户不存在返回 404 / 4001;成员记录不存在返回 404 / 4005。

响应返回更新后的成员记录,role 为新角色,并通知被修改者。

移除团队成员

DELETE /api/v1/teams/{team_id}/members/{user_id}
路径参数类型必填
team_idstring (UUID)是
user_idstring (UUID)是

权限取决于目标是不是自己:

  • 自我移除(user_id 等于当前用户):只需 team:read,相当于退出团队。
  • 移除他人:需要 team:manage 且调用者为 owner/admin。

owner 不能被移除(HTTP 400 / 5209,CANNOT_REMOVE_OWNER);目标用户不存在返回 404 / 4001;成员记录不存在返回 404 / 4005。

成功响应(200 OK):

{
  "code": 0,
  "data": { "user_id": "b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e" },
  "msg": "Team member removed successfully"
}

被移除者与团队都会收到通知。

退出团队

POST /api/v1/teams/{team_id}/leave

需要 team:read(普通成员与 viewer 均可调用)。

  • 不是团队成员返回 HTTP 404 / 3002(NOT_TEAM_MEMBER)。
  • owner 不能退出,返回 HTTP 400 / 5210(OWNER_CANNOT_LEAVE),必须先移交所有权。

成功响应(200 OK):

{ "code": 0, "data": { "team_id": "8f14e45f-..." }, "msg": "You have left the team" }

移交所有权

POST /api/v1/teams/{team_id}/transfer-ownership?new_owner_id=<新所有者 UUID>

new_owner_id 是查询参数,不是请求体字段。写成 JSON body 会被忽略并返回 422(缺少必填查询参数 new_owner_id)。

curl -X POST "https://your-domain.com/api/v1/teams/8f14e45f-ceea-467a-9d0f-1a2b3c4d5e6f/transfer-ownership?new_owner_id=b1c2d3e4-f5a6-4b7c-8d9e-0f1a2b3c4d5e" \
  -H "Authorization: Bearer YOUR_TOKEN"

约束:

  1. 调用者需要 team:manage 权限码且为团队 owner/admin,并额外要求 owner 角色(超级管理员可绕过 owner 要求)。
  2. new_owner_id 对应的用户必须已经是本团队成员,否则 HTTP 404 / 4005。
  3. 新所有者必须具备全局 team:manage 权限,否则 HTTP 403 / 3000。
  4. 不能把所有权移交给当前 owner 自己,返回 HTTP 400 / 5208(CANNOT_PROMOTE_TO_OWNER)。

成功后新所有者角色变为 owner,原所有者自动降为 admin,team.owner 字段同步更新;双方会收到所有权变更通知。响应返回更新后的团队对象。

超级管理员可以把一个没有任何 team:manage 权限的成员设为 owner 吗?不能——第 3 条对被移交给的人同样生效,因此超级管理员也无法绕过新所有者的权限要求。

管理后台:创建与删除团队

创建团队

POST /api/v1/admin/teams

需要 admin:team:create。

字段类型必填说明
namestring是团队名称,需全局唯一
descriptionstring否描述
avatar_urlstring否头像 URL

创建者会成为该团队的 owner 并写入一条 owner 成员记录,因此新团队立即有且只有一个所有者。团队名重复返回 HTTP 400 / 5102。

删除团队

DELETE /api/v1/admin/teams/{team_id}

需要 admin:team:delete。团队不存在返回 404 / 4004;默认团队(is_default = true)不可删除,返回 HTTP 400 / 5205(CANNOT_DELETE_DEFAULT_TEAM)。

响应 data 是被删除的团队对象(用于审计回显),不是 null。

删除团队会一并移除其成员关系与相关授权。团队里的 Agent、工作流、知识库等资源不会自动转移,请在删除前迁移或归档。

团队模型授权

团队能使用哪些模型由团队模型授权记录决定,读写权限不同:

方法路径用途权限
GET/api/v1/teams/{team_id}/models列出已授权模型(可带 model_type 过滤)超级管理员或团队成员
POST/api/v1/teams/{team_id}/models授权单个模型并设置配额仅超级管理员
PUT/api/v1/teams/{team_id}/models/{model_id}修改授权配额与启用状态仅超级管理员
DELETE/api/v1/teams/{team_id}/models/{model_id}撤销单个模型授权仅超级管理员
POST/api/v1/teams/{team_id}/models/batch批量授权仅超级管理员
DELETE/api/v1/teams/{team_id}/models/batch批量撤销仅超级管理员
GET/api/v1/teams/{team_id}/available-models列出已授权且启用的模型(供调用方选择)超级管理员或团队成员
GET/api/v1/teams/{team_id}/models/quota查询每个模型的配额与用量超级管理员或团队成员

三个读取端点对非超级管理员要求「是团队成员」,不要求具体权限码。列表按 priority 倒序返回,priority 越高的授权越靠前。

字段、配额计算与批量请求体详见 Team Models API。

错误码

HTTPcode场景
4001001请求校验失败(缺少 user_id 与 identifier 等)
4033000缺少权限码、viewer 执行写操作,或目标用户缺少 team:manage
4033002不是团队成员
4033003需要团队 owner 或 admin 角色
4033004需要团队 owner 角色(修改角色、移交所有权)
4043002POST /teams/{team_id}/leave 时不是团队成员
4044001目标用户不存在
4044004团队不存在
4044005团队成员记录不存在(含移交时目标不是成员)
4005102团队名已存在
4005103用户已是团队成员
4005205不能删除默认团队
4005206不能直接添加为 owner
4005207不能修改 owner 的角色
4005208不能直接提升为 owner(含移交给当前 owner)
4005209不能移除 owner
4005210owner 必须先移交所有权才能退出

尚未提供的能力

以下是当前版本的边界,请据此设计客户端:

  • 没有独立的成员列表接口:GET /teams/{team_id}/members 不存在,成员列表只在 GET /teams/{team_id} 响应中返回。
  • 没有团队统计接口:团队维度的用量统计请走模型配额接口或后台统计。
  • 平台侧不能创建/删除团队:只能通过 /api/v1/admin/*。
  • 不能通过接口修改团队默认标记:is_default 只读。

代码示例

Python

import os
import requests

BASE = "https://your-domain.com/api/v1"
TOKEN = os.environ["USER_TOKEN"]
HEADERS = {"Authorization": f"Bearer {TOKEN}", "Content-Type": "application/json"}


def list_my_teams() -> list[dict]:
    """列出我加入的团队及我在其中的角色。"""
    result = requests.get(f"{BASE}/teams/my", headers=HEADERS).json()
    if result["code"] != 0:
        raise RuntimeError(f"{result['code']} {result['msg']}")
    return result["data"]


def add_member(team_id: str, identifier: str, role: str = "member") -> dict:
    """通过邮箱精确匹配添加成员。"""
    response = requests.post(
        f"{BASE}/teams/{team_id}/members",
        headers=HEADERS,
        json={"identifier": identifier, "role": role},
    )
    result = response.json()
    if result["code"] != 0:
        raise RuntimeError(f"{result['code']} {result['msg']}")
    return result["data"]


def transfer_ownership(team_id: str, new_owner_id: str) -> dict:
    """移交所有权(new_owner_id 是查询参数)。"""
    response = requests.post(
        f"{BASE}/teams/{team_id}/transfer-ownership",
        headers=HEADERS,
        params={"new_owner_id": new_owner_id},
    )
    result = response.json()
    if result["code"] != 0:
        raise RuntimeError(f"{result['code']} {result['msg']}")
    return result["data"]


# 使用
for team in list_my_teams():
    print(f"{team['name']} — 我的角色: {team['role']}")

JavaScript

const BASE = 'https://your-domain.com/api/v1';

async function listMyTeams(token) {
  const response = await fetch(`${BASE}/teams/my`, {
    headers: { Authorization: `Bearer ${token}` },
  });
  const result = await response.json();
  if (result.code !== 0) throw new Error(`${result.code} ${result.msg}`);
  return result.data;
}

async function addMember(token, teamId, identifier, role = 'member') {
  const response = await fetch(`${BASE}/teams/${teamId}/members`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${token}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({ identifier, role }),
  });
  const result = await response.json();
  if (result.code !== 0) throw new Error(`${result.code} ${result.msg}`);
  return result.data;
}

async function leaveTeam(token, teamId) {
  const response = await fetch(`${BASE}/teams/${teamId}/leave`, {
    method: 'POST',
    headers: { Authorization: `Bearer ${token}` },
  });
  const result = await response.json();
  if (result.code !== 0) throw new Error(`${result.code} ${result.msg}`);
  return result.data;
}

// 使用
const teams = await listMyTeams(process.env.USER_TOKEN);
teams.forEach((team) => console.log(`${team.name} — my role: ${team.role}`));

相关文档

这篇文章对你有帮助吗?

本页目录