API Console
test
Sign in with BES

功能:管理团队成员

做完后,客户管理员在产品(product)的管理页面邀请人、管理成员(membership)和角色(role)。每个调用都带证书和管理员的 access_token,Control 只允许管理员在自己租户(tenant)里能做的事。

环境登录地址带证书调用的接口地址
localhttps://id.local.globalbes.sghttps://id.local.globalbes.sg:19443
testhttps://id.test.globalbes.sghttps://id.test.globalbes.sg:9443
staging待定待定
prod待定待定

Sign in to send requests to see which of these steps are done. Sign in

1. 邀请
1.1

邀请一个人

在哪里做:程序代码

管理员在产品里邀请一个人,得到 invitation_id。对方接受后成为租户成员并带上指定角色;查、重发、撤销邀请时填它。

POSTInvitation Create

程序里发这个请求(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),不能邀请。
1.2

列出邀请

在哪里做:程序代码

列出租户的全部邀请,得到每份邀请和它的状态。管理页面用它显示邀请列表。

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 取下一页。
1.3

查一份邀请

在哪里做:程序代码

按 invitation_id 查一份邀请,得到链接的发送状态。发送失败时重发。

GETInvitation Get

程序里发这个请求(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 已清除。
1.4

重发邀请

在哪里做:程序代码

把同一个邀请链接再排队发一次,得到发送状态回到 pending 的邀请。

POSTInvitation Resend

程序里发这个请求(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 邀请已接受、已撤销或已过期:重新邀请。
1.5

撤销邀请

在哪里做:程序代码

撤销待处理的邀请,得到 revoked 的邀请。链接从此不能再用。

POSTInvitation Revoke

程序里发这个请求(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 邀请已不是待处理状态,不用再撤销。

做完这一部分,手里有:发出的邀请和它们的状态;对方接受后成为租户成员。

2. 成员
2.1

列出成员

在哪里做:程序代码

列出租户的全部成员,包括已移除的,得到成员列表。管理页面用它显示成员。

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 取下一页。
2.2

查一个成员

在哪里做:程序代码

按 membership_id 查一个成员,得到成员状态。移除成员时填这个 membership_id。

GETMembership Get

程序里发这个请求(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
}
2.3

加入已有账号的人

在哪里做:程序代码

把已有 BES 账号的人直接加进租户,得到 membership_id。不用邀请,之后给此人分配角色。

POSTMembership Create

程序里发这个请求(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 已是成员,跳过。
2.4

移除成员

在哪里做:程序代码

移除成员,得到 revoked 的成员和收回的角色分配。此人立即不能再进这个租户。

POSTMembership Revoke

程序里发这个请求(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 账号和其他租户的成员身份不受影响。

做完这一部分,手里有:租户的成员列表,以及加入和移除成员的结果。

3. 角色
3.1

分配角色

在哪里做:程序代码

给成员分配角色,得到 person_role_id。此人从此能用这个角色的功能;收回角色时填它。

POSTRole Assignment Assign

程序里发这个请求(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 租户没有这个产品的有效开通记录。
3.2

收回角色

在哪里做:程序代码

按 person_role_id 收回角色,得到 revoked 的分配。这是此人在该范围的最后一个角色时释放席位。

POSTRole Assignment Revoke

程序里发这个请求(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。
3.3

列出角色的成员

在哪里做:程序代码

列出有这个角色的人,得到角色分配列表。管理页面用它显示角色成员。

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
  }
]
3.4

列出一个人的角色

在哪里做:程序代码

列出一个人的全部角色,包括已收回的,得到角色分配列表。管理页面用它显示此人的权限。

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
  }
]

做完这一部分,手里有:每个人的角色分配;分配和收回时席位跟着变化。