Google Meet 现在会把加入请求分成已验证队列和高风险队列。数据中心里的 bot 全部落入后者。本文介绍我们如何用 SAML 让 bot 登录真实的 Workspace 账户,以及具体怎么配置。

2026 年 4 月,Google Meet 开始把加入请求分成两个队列。受邀者和组织成员进入已验证队列,直接放行。其他所有人落入第二个队列,主持人看到的标签是「存在潜在威胁」,默认按钮是「拒绝」而不是「允许」。
录制 bot 运行在数据中心 IP 上。它们无一例外都落入第二个队列。
我们先试了最直接的办法。住宅代理在这里行不通:IP 池是共享的,信誉早就被消耗光了,Google 照样会标记它们。真正能把 bot 送进已验证队列的只有一件事:成为真实组织里真实的 Google Workspace 用户。
所以我们就做了这件事。现在 bot 可以登录你自己的 Workspace 账户,并以该用户的身份加入会议。
这是按 bot 选择启用的功能。只要不在 request 中传 meet_config,一切照旧:你的 bot 仍然像以前一样匿名加入。
让人意外的一点
大家都以为「已认证 bot」意味着 OAuth,以为要把 Google 账户的访问权交给我们。事实并非如此。
底层机制是 SAML。你的 Workspace 配置一个 Legacy SSO 配置文件,指向我们的登录 endpoint。当 bot 在 Google 登录页输入邮箱时,Google 发现这个域名已经联合认证,于是重定向到我们这里。我们用与你上传到自己 Workspace 的证书相匹配的私钥签署一份 SAML 断言,浏览器把它 POST 回 Google,bot 就登录成功了。
你从未授予 MeetingBaaS 任何 Google API 的访问权限。 你把一份公开证书上传到自己掌控的 Workspace。我们持有对应的私钥,并且只用它做这一件事。
大家通常设想的三种方案都行不通:
| 方案 | 为什么行不通 |
|---|---|
| 用户名和密码 | 会遇到 2FA 提示、密码轮换、验证码,以及「异常登录位置」验证。无头浏览器全都过不去。 |
| Google OAuth(3LO) | 每个账户都需要一个交互式的同意对话框。没有人在那里点击它。 |
| 服务账户 | 根本无法以参会者身份加入 Meet 通话。 |
SAML 绕开了上面每一个问题。没有第二重验证需要满足,因为断言本身就是认证。

两个资源,而不是一个
API 分成 workspaces 和 logins 两部分,这个划分不是随意的。
Google 的 Legacy SSO 配置文件每个 Workspace 只保存一份验证证书,该 Workspace 内的所有用户共用。如果我们把证书建模在 login 上,那么新增一个 bot 账户就只剩两种选择:要么生成新的密钥对并覆盖 Google Admin 中的证书,这会立刻让所有已按旧证书注册的 login 失效;要么手工把同一份证书和密钥粘贴到每一次创建调用里。
所以证书归属于 workspace,各个身份挂在它下面。
/v2/meet-workspaces → domain + SAML certificate and private key
/v2/meet-logins → the Workspace users, attached via workspace_idWorkspaces
POST /v2/meet-workspaces
GET /v2/meet-workspaces
GET /v2/meet-workspaces/:workspace_id
PATCH /v2/meet-workspaces/:workspace_id
DELETE /v2/meet-workspaces/:workspace_id创建方式有两种。可以让我们生成密钥对:
{
"name": "Acme Production",
"domain": "bots.acme.com",
"generate_keypair": true
}也可以自带:
{
"name": "Acme Production",
"domain": "bots.acme.com",
"cert_pem": "-----BEGIN CERTIFICATE-----...",
"private_key_pem": "-----BEGIN PRIVATE KEY-----..."
}两者只能选其一。同时传或者都不传都会报验证错误。创建时我们会检查证书和密钥能否正常解析、密钥是否与证书匹配,以及域名是否为有效主机名且你尚未注册过。
无论你走哪条路径,response 中都会包含 cert_pem,因为这正是你需要粘贴到 Google Admin 里的值。private_key_pem 永远不会返回,任何 endpoint 都不会。
domain 创建后不可修改。其余内容都可以通过 patch 更新,包括同时轮换证书和密钥。
Logins
POST /v2/meet-logins
GET /v2/meet-logins
GET /v2/meet-logins/utilization
GET /v2/meet-logins/:credential_id
PATCH /v2/meet-logins/:credential_id
DELETE /v2/meet-logins/:credential_id{
"workspace_id": "f0e1d2c3-b4a5-6789-0123-456789abcdef",
"name": "Production Bot Pool — Account 1",
"email": "bot1@bots.acme.com",
"email_group": "bots@bots.acme.com"
}邮箱域名必须与父 workspace 的域名一致,或者是它的子域名。email 和 workspace_id 不可修改:要迁移一个 login,请先删除再新建一个。
email_group 是可选的,但值得设置。共享同一分组的 login 会组成一个轮询池;如果你把这个分组地址放进日历邀请,被分配到的 bot 就是受邀者,这是通过等候室最快的路径。
两种资源都接受一个 extra 对象来存放你自己的元数据,之后可以按它过滤:
GET /v2/meet-logins?extra=environment:production在 bot 上使用
{
"meeting_url": "https://meet.google.com/abc-defg-hij",
"bot_name": "Recording Bot",
"meet_config": {
"email_group": "bots@bots.acme.com",
"fallback": "fail"
}
}meet_config 的解析规则:
| 你传入的内容 | 实际行为 |
|---|---|
null(默认值) | 匿名 bot。不登录、不走 SAML,与现有行为一致。 |
{ "email_group": "X" } | 在分组 X 内的活跃 login 之间轮询。 |
{ "credential_id": "Y" } | 固定使用 login Y。 |
| 两者都传,且分组非空 | email_group 优先,credential_id 被忽略。 |
只传 { "email_group": "" } | 在团队全部活跃 login 之间轮询,不做分组过滤。 |
{ "email_group": "", "credential_id": "Y" } | 固定使用 login Y。空分组不算分组,所以 credential_id 生效。 |
分配时会挑选活跃会话最少的 login,数量相同就选最久未使用的,并通过一次原子条件更新占住名额,这样两个同时创建的 bot 不会抢到同一个。
当池子占满时,由 fallback 决定。默认值 "fail" 会返回 FST_ERR_MEET_LOGIN_UNAVAILABLE。"anonymous" 则静默回退到未认证加入。如果拿到某种录像比拿到一份已验证的录像更重要,这就是正确的选择。
容量
每个 login 都带一个并发会话上限,默认是 20。这个数字刻意低于业界认为 Google 开始限流的水平,它只是一个下限,等你测过自己的流量之后可以调高。
GET /v2/meet-logins/utilization 一次调用就给你完整的全貌:
{
"logins_total": 5,
"logins_active": 5,
"logins_invalid": 0,
"concurrent_sessions": 47,
"concurrent_capacity": 100,
"utilization_pct": 47,
"by_email_group": [
{ "email_group": "bots@bots.acme.com", "logins": 5, "concurrent": 47, "capacity": 100 }
]
}盯住 utilization_pct。如果它经常高于 70,说明你需要更多 Workspace 用户,因为流量的尾部已经在触发 fallback 了。
当一个 login 出问题时
workspace 和 login 都有一个 state 字段,取值为 active 或 invalid。只有系统会设置 invalid,而且只在 bot 登录失败且注定会一直失败时才设置:SAML 断言被拒绝,通常意味着 Google Admin 中的证书和我们的不再匹配;或者 Workspace 用户被停用、被删除。临时性超时不会改变任何状态。
被置为无效的 login 不再接受分配,并会记录 last_error_message、last_error_at,以及一个包含触发它的 bot 的 failure_data 对象。重新启用刻意设计成手动操作,等你修复了根本原因之后再执行:
PATCH /v2/meet-logins/:credential_id
{ "state": "active" }PATCH 只接受 "active"。你自己尝试设置 "invalid" 会被 request 校验拒绝,返回 400。
删除仍有会话在进行的 login 或 workspace 会返回 409,而不是把凭据从运行中的 bot 脚下抽走。删除 workspace 会级联删除它下面的 login。
配置步骤
准备一个专用的 Workspace 或子域名
使用单独的 Google Workspace,至少也要用一个专用子域名,例如 bots.acme.com。不要用你的主组织。
Google Admin 中的 SSO 配置是组织级的。如果在员工登录用的 Workspace 上把它指向我们,他们的登录也会被一并重定向。这里需要付费套餐,因为免费版没有 SSO 配置文件。
创建 workspace
curl -X POST https://api.meetingbaas.com/v2/meet-workspaces \
-H "x-meeting-baas-api-key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{"domain": "bots.acme.com", "generate_keypair": true}'保存 response 中的 cert_pem。
在 Google Admin 中配置 SSO
安全(Security)→ 使用第三方 IdP 设置单点登录(Set up single sign-on with a third party IdP)→ 添加 SAML 配置文件(Add SAML profile)→ 旧版 SSO 配置文件(Legacy SSO profile)。
| 字段 | 值 |
|---|---|
| 登录页 URL(Sign-in page URL) | https://api.meetingbaas.com/v2/meet-sso/sign-in |
| 登出页 URL(Sign-out page URL) | https://api.meetingbaas.com/v2/meet-sso/sign-out |
| 验证证书(Verification certificate) | 第 2 步得到的 cert_pem |
| 使用域名专属颁发者(Use a domain-specific issuer) | 是 |
启用这个配置文件,然后在「管理 SSO 配置文件分配」(Manage SSO profile assignments)中完成分配。
创建 Workspace 用户
并发 bot 池的每个名额对应一个 Google Workspace 用户。
每个账户都必须先交互式地完成一次 Google 的「欢迎使用 Google Workspace」引导流程,之后才能正常工作。这一步最容易被跳过,它导致的登录失败看起来像证书问题,其实不是。顺手在 myaccount.google.com 把账户语言设为英语(美国)。
你也可以创建一个 Google 群组,把 bot 用户放进去,再把这个群组加到日历邀请里。
注册每个 login
curl -X POST https://api.meetingbaas.com/v2/meet-logins \
-H "x-meeting-baas-api-key: $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"workspace_id": "...",
"name": "bot1",
"email": "bot1@bots.acme.com",
"email_group": "bots@bots.acme.com"
}'发一个 bot 试试
在 POST /v2/bots 调用中加上 meet_config,然后看着它径直走进会议。
一个值得提前知道的限制
Google 在参会者列表中显示 Workspace 用户本人的名字,会覆盖你传入的 bot_name。如果显示名称对你很重要,目前唯一的变通办法是:想显示几个名字,就配几个 Workspace 用户,这显然扩展不了多远。我们正在研究这个问题。
相关阅读
- 已认证的 Microsoft Teams Bot —— 同样的问题在 Teams 上有另一种解法,以及背后的原因
- Meeting BaaS API 参考文档
想知道自己的流量该配多大的 login 池?或者不确定专用子域名在你的环境里够不够?联系我们。