功能:开通客户
做完后,客户有自己的租户(tenant),能用买下的产品(product),第一位管理员收到登录链接。
| 环境 | 登录地址 | 带证书调用的接口地址 |
|---|---|---|
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
创建租户
在哪里做:程序代码
BOSS 为客户创建租户,得到 tenant_id。后面启用、开通、建管理员都要填它。
程序里发这个请求(id 用前面步骤返回的):
带:证书
请求:
POST https://id.test.globalbes.sg:9443/internal/control/v1/tenants
Content-Type: application/json
{
"request_ref": "create-tenant-topaz",
"code": "topaz",
"display_name": "Topaz Construction"
}
返回:
HTTP/1.1 201 Created
{
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"code": "topaz",
"display_name": "Topaz Construction",
"status": "provisioning",
"created_at": "2026-01-01T08:00:00+00:00",
"mfa_required": false
}
code:客户的短代码,唯一,登记后不再改。subdomain:可选,写完整的已登记主机名,不是前缀。administrator_person_id:可选,写一个已有的在用客户,这个人在同一事务里成为第一位租户管理员;新客户不写,在第 2 部分建人。- 同一个
request_ref重发返回同一个租户。
错误:
| HTTP | 错误码 | 处理 |
|---|---|---|
| 409 | TENANT_CODE_CONFLICT |
这个 code 已被占用,换一个。 |
启用租户
在哪里做:程序代码
把租户改成 active,得到可进入的租户。客户的人登录后才能选它。
程序里发这个请求(id 用前面步骤返回的):
带:证书
请求:
POST https://id.test.globalbes.sg:9443/internal/control/v1/tenants/7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11/status
Content-Type: application/json
{
"request_ref": "activate-tenant-topaz",
"status": "active"
}
返回:
HTTP/1.1 200 OK
{
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"code": "topaz",
"display_name": "Topaz Construction",
"status": "active",
"created_at": "2026-01-01T08:00:00+00:00",
"mfa_required": false
}
suspended暂停访问,不删除任何东西;decommissioning不能恢复。
创建项目
在哪里做:程序代码
项目级产品用:创建项目和它的开通记录,得到 project_id 和待启用的 entitlement_id。启用开通记录时填它们。
程序里发这个请求(id 用前面步骤返回的):
带:证书
请求:
POST https://id.test.globalbes.sg:9443/internal/control/v1/projects
Content-Type: application/json
{
"request_ref": "create-project-harbour-tower",
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"display_name": "Harbour Tower",
"initial_entitlement": {
"product_id": "b41d9a70-6c2e-4a0f-8e7d-52a9c3f0d614",
"starts_at": "2026-01-01T00:00:00Z",
"ends_at": "2027-01-01T00:00:00Z",
"source": "subscription",
"source_ref": "order-10027",
"source_version": 1
}
}
返回:
HTTP/1.1 201 Created
{
"project": {
"project_id": "e2f7a1c4-0d35-4b8e-a6c9-7d18f4b2a903",
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"display_name": "Harbour Tower",
"status": "active",
"version": 0
},
"entitlement": {
"entitlement_id": "9e4b2d7a-1c58-4f36-b0e9-a7d3c5f81b24",
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"product_id": "b41d9a70-6c2e-4a0f-8e7d-52a9c3f0d614",
"project_id": "e2f7a1c4-0d35-4b8e-a6c9-7d18f4b2a903",
"starts_at": "2026-01-01T00:00:00+00:00",
"ends_at": "2027-01-01T00:00:00+00:00",
"source": "subscription",
"source_ref": "order-10027",
"source_version": 1,
"seat_limit": null,
"status": "pending",
"readiness": "pending",
"readiness_ref": null,
"revoked_at": null,
"version": 0
}
}
- 产品的
scope_level是tenant时跳过这一步。 - 项目和它的第一条开通记录一起创建;
project_id是项目唯一的标识,没有项目代码。
创建开通记录
在哪里做:程序代码
租户级产品用:给租户开通产品,得到待启用的 entitlement_id。下一步启用时填它。
程序里发这个请求(id 用前面步骤返回的):
带:证书
请求:
POST https://id.test.globalbes.sg:9443/internal/control/v1/entitlements
Content-Type: application/json
{
"request_ref": "create-topaz-cost-entitlement",
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"entitlement": {
"product_id": "8a3e5c17-2f94-4b6d-a0c8-5e71d9b3f246",
"starts_at": "2026-01-01T00:00:00Z",
"ends_at": "2027-01-01T00:00:00Z",
"source": "subscription",
"source_ref": "order-10028",
"source_version": 1,
"seat_limit": 50
}
}
返回:
HTTP/1.1 201 Created
{
"entitlement_id": "0d5f3b8e-2a69-4c17-8e4b-b6f1d9a3c752",
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"product_id": "8a3e5c17-2f94-4b6d-a0c8-5e71d9b3f246",
"project_id": null,
"starts_at": "2026-01-01T00:00:00+00:00",
"ends_at": "2027-01-01T00:00:00+00:00",
"source": "subscription",
"source_ref": "order-10028",
"source_version": 1,
"seat_limit": 50,
"status": "pending",
"readiness": "pending",
"readiness_ref": null,
"revoked_at": null,
"version": 0
}
- 项目级产品跳过这一步:
project.mint已建好开通记录。 source、source_ref、source_version:这段时间出自 BOSS 里哪份订单或合同。ends_at必填;同一租户、产品和项目的时段不能重叠。seat_limit可选,是能分配角色(role)的人数上限。
错误:
| HTTP | 错误码 | 处理 |
|---|---|---|
| 409 | ENTITLEMENT_PERIOD_OVERLAP |
与已有开通记录的时段重叠,调整 starts_at、ends_at。 |
启用开通记录
在哪里做:程序代码
启用开通记录,得到生效的开通。客户的人登录后能进入产品。
POSTEntitlement Activate程序里发这个请求(id 用前面步骤返回的):
带:证书
请求:
POST https://id.test.globalbes.sg:9443/internal/control/v1/entitlements/9e4b2d7a-1c58-4f36-b0e9-a7d3c5f81b24/activate
Content-Type: application/json
{
"request_ref": "activate-topaz-bim-entitlement",
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"product_id": "b41d9a70-6c2e-4a0f-8e7d-52a9c3f0d614",
"project_id": "e2f7a1c4-0d35-4b8e-a6c9-7d18f4b2a903",
"runtime_ready": true
}
返回:
HTTP/1.1 200 OK
{
"entitlement_id": "9e4b2d7a-1c58-4f36-b0e9-a7d3c5f81b24",
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"product_id": "b41d9a70-6c2e-4a0f-8e7d-52a9c3f0d614",
"project_id": "e2f7a1c4-0d35-4b8e-a6c9-7d18f4b2a903",
"starts_at": "2026-01-01T00:00:00+00:00",
"ends_at": "2027-01-01T00:00:00+00:00",
"source": "subscription",
"source_ref": "order-10027",
"source_version": 1,
"seat_limit": null,
"status": "active",
"readiness": "ready",
"readiness_ref": "4bf92f3577b34da6a3ce929d0e0e4736",
"revoked_at": null,
"version": 1
}
runtime_ready: true表示产品为这个客户准备的运行环境已就绪。
错误:
| HTTP | 错误码 | 处理 |
|---|---|---|
| 409 | ENTITLEMENT_NOT_READY |
产品为客户准备的环境就绪后,runtime_ready 写 true 再启用。 |
做完这一部分,手里有:启用的客户租户、项目(项目级产品)和生效的开通记录。客户的人登录后能进入产品。
创建客户管理员
在哪里做:程序代码
在客户租户里创建第一位管理员,得到 person_id。发首次登录链接时填它。
程序里发这个请求(id 用前面步骤返回的):
带:证书
请求:
POST https://id.test.globalbes.sg:9443/internal/control/v1/people
Content-Type: application/json
{
"request_ref": "create-person-marcus-lim",
"tenant_id": "7c0e2a5e-3b1f-4d6a-9a53-1f2f6d0c8a11",
"display_name": "Marcus Lim",
"channels": [
{
"channel_type": "email",
"channel_identifier": "marcus.lim@topaz.example.com"
}
],
"role_id": "b7d3f9a1-2e64-4c08-a5b9-d1f7c3e5a862"
}
返回:
HTTP/1.1 201 Created
{
"person_id": "5a9d3c1b-7e24-4f60-b8a1-c03e6d2f9475",
"membership_id": "3b8d1f6a-7c24-4e95-a0b3-d5e2f9c84160",
"person_role_id": "8c4e0a26-f1b7-4d39-a5c2-e7f3b9d1a648",
"channel_identity_ids": [
"6b1f4e9a-8c30-4d72-a5e6-f09c3b7d1e24"
]
}
- 一次事务里建好人、地址(未验证)、租户成员(membership)和角色分配,这时还不发任何东西。
role_id:Control 产品(code为control)的role.list里code为tenant-administrator的角色。
错误:
| HTTP | 错误码 | 处理 |
|---|---|---|
| 409 | CHANNEL_IDENTITY_BOUND |
此人已有 BES 账号:用 membership.create 加进租户。 |
发首次登录链接
在哪里做:程序代码
给管理员发首次登录链接,得到 action_id。管理员用链接设好密码后登录;用 action_id 查链接状态。
程序里发这个请求(id 用前面步骤返回的):
带:证书
请求:
POST https://id.test.globalbes.sg:9443/internal/control/v1/credentials/recoveries
Content-Type: application/json
{
"request_ref": "first-login-marcus-lim",
"person_id": "5a9d3c1b-7e24-4f60-b8a1-c03e6d2f9475",
"purpose": "initial_login",
"reason": "Topaz Construction onboarding"
}
返回:
HTTP/1.1 201 Created
{
"action_id": "1f7b3d9e-5c28-4a64-b0e3-c8d2a6f4e193",
"purpose": "initial_login",
"expires_at": "2026-01-03T08:00:00+00:00",
"delivery": "queued"
}
- Control 通过 Messaging 把链接发到此人的地址;返回里没有链接。
- 首次登录链接最长 48 小时有效。
错误:
| HTTP | 错误码 | 处理 |
|---|---|---|
| 409 | CREDENTIAL_CONFLICT |
此人已设过密码,不用再发。 |
查链接状态
在哪里做:程序代码
用 action_id 查链接状态,得到 pending、completed 等状态。过期了就重发。
程序里发这个请求(id 用前面步骤返回的):
带:证书
请求:
GET https://id.test.globalbes.sg:9443/internal/control/v1/credential-actions/1f7b3d9e-5c28-4a64-b0e3-c8d2a6f4e193
返回:
HTTP/1.1 200 OK
{
"action_id": "1f7b3d9e-5c28-4a64-b0e3-c8d2a6f4e193",
"status": "pending",
"expires_at": "2026-01-03T08:00:00+00:00"
}
status:pending还没用,completed已激活,expired过期了要重发,revoked已撤销。
做完这一部分,手里有:管理员的 person_id 和首次登录链接的 action_id;管理员激活后能登录。