功能:管理团队成员
做完后,客户管理员在产品(product)的管理页面邀请人、管理成员(membership)和角色(role)。每个调用都带证书和管理员的 access_token,Control 只允许管理员在自己租户(tenant)里能做的事。
| 环境 | 登录地址 | 带证书调用的接口地址 |
|---|---|---|
local | https://id.local.globalbes.sg | https://id.local.globalbes.sg:19443 |
test | https://id.test.globalbes.sg | https://id.test.globalbes.sg:9443 |
staging | 待定 | 待定 |
prod | 待定 | 待定 |
Sign in to send requests to see which of these steps are done. Sign in
邀请一个人
在哪里做:程序代码
管理员在产品里邀请一个人,得到 invitation_id。对方接受后成为租户成员并带上指定角色;查、重发、撤销邀请时填它。
程序里发这个请求(id 用前面步骤返回的):
带:证书 + 用户的 access_token
请求:
POST https://id.test.globalbes.sg:9443/internal/control/v1/invitations
Authorization: Bearer <access_token>
Content-Type: application/json
{
"request_ref": "invite-site-engineer",
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"project_id": "e2f7a1c4-0d35-4b8e-a6c9-7d18f4b2a903",
"role_id": "5c2f8a1d-6e93-4b07-8c4a-d1e6f3b92a75",
"target": {
"email": "site.engineer@topaz.example.com"
},
"expires_at": "2026-01-03T08:00:00+00:00"
}
返回:
HTTP/1.1 201 Created
{
"invitation": {
"invitation_id": "e6c2a8f4-0d39-4b51-8e7a-b3f9d1c5a206",
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"project_id": "e2f7a1c4-0d35-4b8e-a6c9-7d18f4b2a903",
"role_id": "5c2f8a1d-6e93-4b07-8c4a-d1e6f3b92a75",
"target": {
"email": "site.engineer@topaz.example.com",
"channel_type": null,
"channel_identifier": null
},
"invited_by": "5a9d3c1b-7e24-4f60-b8a1-c03e6d2f9475",
"status": "pending",
"expires_at": "2026-01-03T08:00:00+00:00",
"accepted_person_id": null,
"created_at": "2026-01-01T08:00:00+00:00"
}
}
- 带管理员的 access_token;不带时在
X-BES-Operator写管理员的person_id。邀请一定有邀请人。 role_id是本产品的角色;项目(project)级产品同时写project_id。expires_at最多是创建后 48 小时。- 同一地址已有待处理的邀请时,返回那份邀请。
- Control 通过 Messaging 发邀请链接,返回里没有链接。
错误:
| HTTP | 错误码 | 处理 |
|---|---|---|
| 403 | ENTITLEMENT_MISSING |
租户没有这个产品的有效开通记录(entitlement),不能邀请。 |
列出邀请
在哪里做:程序代码
列出租户的全部邀请,得到每份邀请和它的状态。管理页面用它显示邀请列表。
GETInvitation List程序里发这个请求(id 用前面步骤返回的):
带:证书 + 用户的 access_token
请求:
GET https://id.test.globalbes.sg:9443/internal/control/v1/tenants/7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11/invitations?limit=50 Authorization: Bearer <access_token>
返回:
HTTP/1.1 200 OK
{
"entries": [
{
"invitation_id": "e6c2a8f4-0d39-4b51-8e7a-b3f9d1c5a206",
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"project_id": "e2f7a1c4-0d35-4b8e-a6c9-7d18f4b2a903",
"role_id": "5c2f8a1d-6e93-4b07-8c4a-d1e6f3b92a75",
"target": {
"email": "site.engineer@topaz.example.com",
"channel_type": null,
"channel_identifier": null
},
"invited_by": "5a9d3c1b-7e24-4f60-b8a1-c03e6d2f9475",
"status": "pending",
"expires_at": "2026-01-03T08:00:00+00:00",
"accepted_person_id": null,
"created_at": "2026-01-01T08:00:00+00:00"
}
],
"next_cursor": null
}
next_cursor不为null时,带上它作为cursor取下一页。
查一份邀请
在哪里做:程序代码
按 invitation_id 查一份邀请,得到链接的发送状态。发送失败时重发。
程序里发这个请求(id 用前面步骤返回的):
带:证书 + 用户的 access_token
请求:
GET https://id.test.globalbes.sg:9443/internal/control/v1/invitations/e6c2a8f4-0d39-4b51-8e7a-b3f9d1c5a206 Authorization: Bearer <access_token>
返回:
HTTP/1.1 200 OK
{
"invitation_id": "e6c2a8f4-0d39-4b51-8e7a-b3f9d1c5a206",
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"project_id": "e2f7a1c4-0d35-4b8e-a6c9-7d18f4b2a903",
"role_id": "5c2f8a1d-6e93-4b07-8c4a-d1e6f3b92a75",
"target": {
"email": "site.engineer@topaz.example.com",
"channel_type": null,
"channel_identifier": null
},
"invited_by": "5a9d3c1b-7e24-4f60-b8a1-c03e6d2f9475",
"status": "pending",
"expires_at": "2026-01-03T08:00:00+00:00",
"accepted_person_id": null,
"created_at": "2026-01-01T08:00:00+00:00",
"delivery_status": "sent",
"delivered_at": "2026-01-01T08:00:05+00:00"
}
delivery_status:pending待发,sent已发,failed发送失败,cleared已清除。
重发邀请
在哪里做:程序代码
把同一个邀请链接再排队发一次,得到发送状态回到 pending 的邀请。
程序里发这个请求(id 用前面步骤返回的):
带:证书 + 用户的 access_token
请求:
POST https://id.test.globalbes.sg:9443/internal/control/v1/invitations/e6c2a8f4-0d39-4b51-8e7a-b3f9d1c5a206/resend
Authorization: Bearer <access_token>
Content-Type: application/json
{
"request_ref": "resend-site-engineer-1"
}
返回:
HTTP/1.1 200 OK
{
"invitation_id": "e6c2a8f4-0d39-4b51-8e7a-b3f9d1c5a206",
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"project_id": "e2f7a1c4-0d35-4b8e-a6c9-7d18f4b2a903",
"role_id": "5c2f8a1d-6e93-4b07-8c4a-d1e6f3b92a75",
"target": {
"email": "site.engineer@topaz.example.com",
"channel_type": null,
"channel_identifier": null
},
"invited_by": "5a9d3c1b-7e24-4f60-b8a1-c03e6d2f9475",
"status": "pending",
"expires_at": "2026-01-03T08:00:00+00:00",
"accepted_person_id": null,
"created_at": "2026-01-01T08:00:00+00:00",
"delivery_status": "pending",
"delivered_at": null
}
- 链接已发出或发送失败后才能重发。
错误:
| HTTP | 错误码 | 处理 |
|---|---|---|
| 410 | INVITATION_INVALID |
邀请已接受、已撤销或已过期:重新邀请。 |
撤销邀请
在哪里做:程序代码
撤销待处理的邀请,得到 revoked 的邀请。链接从此不能再用。
程序里发这个请求(id 用前面步骤返回的):
带:证书 + 用户的 access_token
请求:
POST https://id.test.globalbes.sg:9443/internal/control/v1/invitations/e6c2a8f4-0d39-4b51-8e7a-b3f9d1c5a206/revoke
Authorization: Bearer <access_token>
Content-Type: application/json
{
"request_ref": "revoke-site-engineer"
}
返回:
HTTP/1.1 200 OK
{
"invitation_id": "e6c2a8f4-0d39-4b51-8e7a-b3f9d1c5a206",
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"project_id": "e2f7a1c4-0d35-4b8e-a6c9-7d18f4b2a903",
"role_id": "5c2f8a1d-6e93-4b07-8c4a-d1e6f3b92a75",
"target": {
"email": "site.engineer@topaz.example.com",
"channel_type": null,
"channel_identifier": null
},
"invited_by": "5a9d3c1b-7e24-4f60-b8a1-c03e6d2f9475",
"status": "revoked",
"expires_at": "2026-01-03T08:00:00+00:00",
"accepted_person_id": null,
"created_at": "2026-01-01T08:00:00+00:00"
}
错误:
| HTTP | 错误码 | 处理 |
|---|---|---|
| 410 | INVITATION_INVALID |
邀请已不是待处理状态,不用再撤销。 |
做完这一部分,手里有:发出的邀请和它们的状态;对方接受后成为租户成员。
列出成员
在哪里做:程序代码
列出租户的全部成员,包括已移除的,得到成员列表。管理页面用它显示成员。
GETMembership List程序里发这个请求(id 用前面步骤返回的):
带:证书 + 用户的 access_token
请求:
GET https://id.test.globalbes.sg:9443/internal/control/v1/tenants/7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11/memberships?limit=50 Authorization: Bearer <access_token>
返回:
HTTP/1.1 200 OK
{
"entries": [
{
"membership_id": "3b8d1f6a-7c24-4e95-a0b3-d5e2f9c84160",
"person_id": "5a9d3c1b-7e24-4f60-b8a1-c03e6d2f9475",
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"status": "active",
"membership_version": 0,
"binding_version": 0,
"created_at": "2026-01-01T08:00:00+00:00",
"revoked_at": null
}
],
"next_cursor": null
}
status为active或revoked。next_cursor不为null时,带上它作为cursor取下一页。
查一个成员
在哪里做:程序代码
按 membership_id 查一个成员,得到成员状态。移除成员时填这个 membership_id。
程序里发这个请求(id 用前面步骤返回的):
带:证书 + 用户的 access_token
请求:
GET https://id.test.globalbes.sg:9443/internal/control/v1/memberships/3b8d1f6a-7c24-4e95-a0b3-d5e2f9c84160 Authorization: Bearer <access_token>
返回:
HTTP/1.1 200 OK
{
"membership_id": "3b8d1f6a-7c24-4e95-a0b3-d5e2f9c84160",
"person_id": "5a9d3c1b-7e24-4f60-b8a1-c03e6d2f9475",
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"status": "active",
"membership_version": 0,
"binding_version": 0,
"created_at": "2026-01-01T08:00:00+00:00",
"revoked_at": null
}
加入已有账号的人
在哪里做:程序代码
把已有 BES 账号的人直接加进租户,得到 membership_id。不用邀请,之后给此人分配角色。
程序里发这个请求(id 用前面步骤返回的):
带:证书 + 用户的 access_token
请求:
POST https://id.test.globalbes.sg:9443/internal/control/v1/memberships
Authorization: Bearer <access_token>
Content-Type: application/json
{
"request_ref": "add-member-site-engineer",
"person_id": "4a8c2e6f-1b39-4d75-a0e8-c6f2b4d8e157",
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11"
}
返回:
HTTP/1.1 201 Created
{
"membership_id": "9f3b7d1e-6c48-4a20-b5e9-d2a6c8f4b031",
"person_id": "4a8c2e6f-1b39-4d75-a0e8-c6f2b4d8e157",
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"status": "active",
"membership_version": 0,
"binding_version": 0,
"created_at": "2026-01-02T08:00:00+00:00",
"revoked_at": null
}
- 已移除的成员会恢复为
active,membership_id不变。
错误:
| HTTP | 错误码 | 处理 |
|---|---|---|
| 409 | MEMBERSHIP_ALREADY_ACTIVE |
已是成员,跳过。 |
移除成员
在哪里做:程序代码
移除成员,得到 revoked 的成员和收回的角色分配。此人立即不能再进这个租户。
程序里发这个请求(id 用前面步骤返回的):
带:证书 + 用户的 access_token
请求:
POST https://id.test.globalbes.sg:9443/internal/control/v1/memberships/9f3b7d1e-6c48-4a20-b5e9-d2a6c8f4b031/revoke
Authorization: Bearer <access_token>
Content-Type: application/json
{
"request_ref": "revoke-member-site-engineer",
"reason": "Left Topaz Construction"
}
返回:
HTTP/1.1 200 OK
{
"membership": {
"membership_id": "9f3b7d1e-6c48-4a20-b5e9-d2a6c8f4b031",
"person_id": "4a8c2e6f-1b39-4d75-a0e8-c6f2b4d8e157",
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"status": "revoked",
"membership_version": 1,
"binding_version": 1,
"created_at": "2026-01-02T08:00:00+00:00",
"revoked_at": "2026-02-01T08:00:00+00:00"
},
"person_role_ids": [
"f1b5d9e3-7c46-4a82-9d1e-c4a8b2f6e379"
]
}
- 同一事务里收回此人在本租户的全部角色分配(返回的
person_role_ids),此人在本租户打开的会话不再通过验证。 - 此人的 BES 账号和其他租户的成员身份不受影响。
做完这一部分,手里有:租户的成员列表,以及加入和移除成员的结果。
分配角色
在哪里做:程序代码
给成员分配角色,得到 person_role_id。此人从此能用这个角色的功能;收回角色时填它。
程序里发这个请求(id 用前面步骤返回的):
带:证书 + 用户的 access_token
请求:
POST https://id.test.globalbes.sg:9443/internal/control/v1/role-assignments
Authorization: Bearer <access_token>
Content-Type: application/json
{
"request_ref": "assign-document-controller-site-engineer",
"product_id": "b41d9a70-6c2e-4a0f-8e7d-52a9c3f0d614",
"person_id": "4a8c2e6f-1b39-4d75-a0e8-c6f2b4d8e157",
"role_id": "5c2f8a1d-6e93-4b07-8c4a-d1e6f3b92a75",
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"project_id": "e2f7a1c4-0d35-4b8e-a6c9-7d18f4b2a903"
}
返回:
HTTP/1.1 201 Created
{
"person_role_id": "f1b5d9e3-7c46-4a82-9d1e-c4a8b2f6e379",
"person_id": "4a8c2e6f-1b39-4d75-a0e8-c6f2b4d8e157",
"role_id": "5c2f8a1d-6e93-4b07-8c4a-d1e6f3b92a75",
"product_id": "b41d9a70-6c2e-4a0f-8e7d-52a9c3f0d614",
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"project_id": "e2f7a1c4-0d35-4b8e-a6c9-7d18f4b2a903",
"status": "active",
"revoked_at": null,
"version": 0
}
- 此人必须是在用成员,租户对这个产品要有有效的开通记录。
- 席位按人数算:同一个人在同一范围的第二个角色不再占席位。
- 项目级产品写
project_id;不写时只指租户级分配。
错误:
| HTTP | 错误码 | 处理 |
|---|---|---|
| 409 | SEAT_LIMIT_EXCEEDED |
席位已满:先收回别人的角色,或扩大开通记录的席位。 |
| 403 | ACTIVE_MEMBERSHIP_REQUIRED |
先把此人加为成员。 |
| 403 | ENTITLEMENT_MISSING |
租户没有这个产品的有效开通记录。 |
收回角色
在哪里做:程序代码
按 person_role_id 收回角色,得到 revoked 的分配。这是此人在该范围的最后一个角色时释放席位。
程序里发这个请求(id 用前面步骤返回的):
带:证书 + 用户的 access_token
请求:
POST https://id.test.globalbes.sg:9443/internal/control/v1/role-assignments/f1b5d9e3-7c46-4a82-9d1e-c4a8b2f6e379/revoke
Authorization: Bearer <access_token>
Content-Type: application/json
{
"request_ref": "revoke-document-controller-site-engineer",
"product_id": "b41d9a70-6c2e-4a0f-8e7d-52a9c3f0d614",
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"project_id": "e2f7a1c4-0d35-4b8e-a6c9-7d18f4b2a903"
}
返回:
HTTP/1.1 200 OK
{
"person_role_id": "f1b5d9e3-7c46-4a82-9d1e-c4a8b2f6e379",
"person_id": "4a8c2e6f-1b39-4d75-a0e8-c6f2b4d8e157",
"role_id": "5c2f8a1d-6e93-4b07-8c4a-d1e6f3b92a75",
"product_id": "b41d9a70-6c2e-4a0f-8e7d-52a9c3f0d614",
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"project_id": "e2f7a1c4-0d35-4b8e-a6c9-7d18f4b2a903",
"status": "revoked",
"revoked_at": "2026-02-01T08:00:00+00:00",
"version": 1
}
- 路径里的
assignment_id是分配时返回的person_role_id。
列出角色的成员
在哪里做:程序代码
列出有这个角色的人,得到角色分配列表。管理页面用它显示角色成员。
GETRole Member List程序里发这个请求(id 用前面步骤返回的):
带:证书 + 用户的 access_token
请求:
GET https://id.test.globalbes.sg:9443/internal/control/v1/roles/5c2f8a1d-6e93-4b07-8c4a-d1e6f3b92a75/members?tenant_id=7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11&project_id=e2f7a1c4-0d35-4b8e-a6c9-7d18f4b2a903 Authorization: Bearer <access_token>
返回:
HTTP/1.1 200 OK
[
{
"person_role_id": "f1b5d9e3-7c46-4a82-9d1e-c4a8b2f6e379",
"person_id": "4a8c2e6f-1b39-4d75-a0e8-c6f2b4d8e157",
"role_id": "5c2f8a1d-6e93-4b07-8c4a-d1e6f3b92a75",
"product_id": "b41d9a70-6c2e-4a0f-8e7d-52a9c3f0d614",
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"project_id": "e2f7a1c4-0d35-4b8e-a6c9-7d18f4b2a903",
"status": "active",
"revoked_at": null,
"version": 0
}
]
列出一个人的角色
在哪里做:程序代码
列出一个人的全部角色,包括已收回的,得到角色分配列表。管理页面用它显示此人的权限。
GETPerson Role List程序里发这个请求(id 用前面步骤返回的):
带:证书 + 用户的 access_token
请求:
GET https://id.test.globalbes.sg:9443/internal/control/v1/people/4a8c2e6f-1b39-4d75-a0e8-c6f2b4d8e157/roles?tenant_id=7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11 Authorization: Bearer <access_token>
返回:
HTTP/1.1 200 OK
[
{
"person_role_id": "f1b5d9e3-7c46-4a82-9d1e-c4a8b2f6e379",
"person_id": "4a8c2e6f-1b39-4d75-a0e8-c6f2b4d8e157",
"role_id": "5c2f8a1d-6e93-4b07-8c4a-d1e6f3b92a75",
"product_id": "b41d9a70-6c2e-4a0f-8e7d-52a9c3f0d614",
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"project_id": "e2f7a1c4-0d35-4b8e-a6c9-7d18f4b2a903",
"status": "active",
"revoked_at": null,
"version": 0
}
]
做完这一部分,手里有:每个人的角色分配;分配和收回时席位跟着变化。