All posts

本文译自英文原文。 阅读原版

WhatsApp 平台

电话号码已经不再是 WhatsApp 识别你客户的方式了。大多数 CRM 还没跟上。

WhatsApp 的 Business-Scoped User ID 已经在 Cloud API 里取代电话号码,成为身份主键。这里是一份逐字段的对照表,告诉你 CRM 必须改什么。

Same Tham联合创始人 / 首席营销官,StaffOS
·
·
13 min read
·
Updated 8 6月 2026

WhatsApp 正在把客户身份从电话号码上迁走,而如果你在运营一个 CRM 或任何一种消息集成,这个变更已经落到你头上了。自 2026 年 3 月底到 4 月初的这次推出以来,每一条 WhatsApp Cloud API 的 message webhook 都携带了一个叫 Business-Scoped User ID(简称 BSUID)的新身份字段,那是 Meta 按 business portfolio 签发的一个不透明 token。[1][2] 它作为自己的字段出现,而不是塞进旧的电话字段里的一个新值,并且一旦用户挑了一个 WhatsApp username,电话号码本身现在就可能缺失。如果读完这篇文章你只改一件事,就改这件:在每一条入站消息上把 BSUID 当作身份主键,把电话号码保留为一个可空的别名,并且不要再假设 tofromwa_id 会装着电话号码。[2] 本文剩下的部分就是那份逐字段的对照表,对照过主文档核验过,外加那些已经在流传的错误做法。

有两件事正在同时落地,而几乎所有人都把它们混着跑。一件是面向用户的隐私功能,也就是 WhatsApp username,它让某个人不必交出号码就能联系到一家业务。另一件是面向开发者的身份变更,也就是 BSUID,无论某个用户是否设置过 username,它都会出现在 Cloud API 里。username 抢了头条,但 BSUID 才是那个悄悄弄坏集成的家伙,所以把这两件事分开看是值得的。它们跑在不同的时间线上,触碰你技术栈里不同的部分。

两层,不是一层

大多数报道把「WhatsApp usernames」和「BSUID」当作一次发布。它们其实是两层,瞄准不同的人,影响半径也大相径庭。

它是什么 影响谁 截至 2026 年 6 月的状态
WhatsApp username 一个可选的公开 handle,让用户不必分享电话号码就能被联系到 [2][6] 终端用户(opt-in,没有人被强制采用) 自 2026 年 4 月起限量 beta,更广泛的推出从 2026 年 6 月开始 [2][3][5]
Business-Scoped User ID(BSUID) Cloud API 里一个新的不透明身份 token,对每一个用户都存在,无论是否采用 username [1][2] 平台上的每一家业务、每一个 CRM 和每一个 BSP 集成 自 2026 年 3 月底到 4 月初的推出以来,已在 message webhooks 中上线 [1][2][5]

正是那个时间差把团队绊倒了。你不能等 username 推完了再去做这项工作,因为开发者那一侧已经发货了。BSUID 现在就在你的 webhooks 里,包括那些永远不会去碰 username 功能的用户。

推出节奏,带日期

下面这些标注了日期的里程碑,取自那些镜像了 Meta 合作方指引的 BSP 文档。凡是某个日期是「正在推进中」而不是一个固定切换点的,我们都会说明。

日期 里程碑
2026 年 3 月底 / 4 月初 BSUID 开始出现在 Cloud API 和 BSP 的 message webhooks 里。Meta 的主页面写的是 4 月初;Azure 和 360dialog 的时间线表写的是 3 月 31 日 [1][2][5] 开发者
2026 年 4 月初 Contact Book 上线:一个由 Meta 托管、默认开启的 phone-to-BSUID 映射存储 [2] 开发者
2026 年 4 月 WhatsApp username 对一小撮用户限量 beta [3][5] 用户
2026 年 5 月(确切日期待定) Meta 的直连 Cloud API 开始通过新的 recipient 字段支持发送到一个 BSUID [1] 开发者
2026 年 6 月 username 开始触达终端用户,业务可以认领 handle;Azure 和 Twilio 把它们包装层的发送可用性框定在这附近 [2][3] 用户
2026 年余下时间 分阶段、一国接一国地向广泛可用扩展 [5] 用户

有一个日期值得加一句注解,而它正好干净地演示了本文反复回到的那个「原始 API 对比 BSP」的分裂。Meta 自己的 BSUID 页面说,直连 Cloud API 要到 2026 年 5 月才会支持发送到一个 BSUID,确切日期待定。[1] Azure 和 Twilio 把生产可用性框定在 2026 年 6 月前后,绑定到 username 触达终端用户。[2][3] 所以把外发可用性当作因供应商而异的事:原始契约是新的 recipient 字段,但你实际什么时候能用上它,取决于你是在直连 API 上还是在某个 BSP 上,而两者各自可以在自己的日期落地。和你的供应商确认你的情况。

入站 webhook 实际长什么样

这正是大多数文章写错的地方。在直连 Cloud API 上,BSUID 是它自己的字段。它不是旧的 wa_id 携带了一种新值。

字段(原始 Cloud API) 携带 何时存在
contacts[].user_id 用户的 BSUID 总是,在每一条 message webhook 里 [1][6]
messages[].from_user_id 发送者的 BSUID 总是,在每一条 message webhook 里 [1]
contacts[].profile.username WhatsApp username 仅当用户设置了一个时 [1][6]
contacts[].wa_id 电话号码 当电话号码可用时;一旦采用了 username 且电话号码包含条件不满足就会被省略 [2]
messages[].from 发送者的电话号码 wa_id 同样的条件 [2]
statuses[].recipient_user_id 收件人的 BSUID 在 status webhooks 上,无论原始发送用的是电话号码还是 BSUID。唯一的省略情况:一条原本以电话号码寻址的消息所对应的失败状态 [1][6]

要记住的形态是:user_idfrom_user_id 总是在场,而电话字段不一定。当电话号码可用时,你会一次拿到两者,电话号码在 wa_idfrom 里,BSUID 在 user_idfrom_user_id 里。[2] 一旦某个用户采用了 username 并且和你没有近期往来历史,电话字段就可能空着或缺失过来,而 BSUID 是唯一能标识发送者的东西。

只要下列条件中至少有一个成立,Meta 就会让电话号码继续流过来,这是按 business 的电话号码而不是按 portfolio 来核验的:[2]

  • 你在过去 30 天内给该用户的号码发过消息或打过电话。
  • 你在过去 30 天内收到过来自该用户号码的消息或来电。
  • 该用户在你的 Contact Book 里。

所以一段进行中的对话没问题。号码缺失发生在那些全新的、只有 username 的、没有历史的联系人身上,而这恰好就是一家做 lead-gen 的业务整天都在打交道的那一类人:一个你从没说过话的人发来的第一条消息。

直连 Cloud API 的名字和你 BSP 的名字不一样

如果你不在直连 Cloud API 上,你的 Business Solution Provider 已经把这些字段重命名了。读一份 BSP 集成指南,然后跑到一个原始 Cloud API 负载里去找那些名字,是一条常见的死路。

表面 BSUID 字段名 来源
直连 Cloud API contacts[].user_idmessages[].from_user_id [1]
Twilio ExternalUserId [3]
Infobip contact.userId [4]
Azure Communication Services fromBSUID(入站)和 toBSUID(投递状态) [2]

底下不管哪种方式都是同一个不透明的 BSUID。变的只是包在它外面的那一层。

外发寻址:端点不变,但原始 API 增加了 recipient

发送端点不变,但原始 Cloud API 的字段变了。电话号码发送仍然用 to。BSUID 发送用一个新的 recipient 字段。[1] 两者至少要有一个在场,如果你两者都发,Meta 让 to 优先,所以它们是两个独立的寻址输入,而不是一个会自动判别你递进去什么的单一字段。[1] 这正是 BSP 包装层和直连契约分歧最大的地方:比如 Azure Communication Services 在它那一个 to 数组里既收电话号码也收 BSUID,并替你分拣好 [2],这是个方便的归一化处理,但不是原始 API 的做法。如果你的文章自称在描绘直连 Cloud API(就像这篇这样),那 recipient 才是要紧的字段。

格式两边都很挑剔。一个 BSUID 是一个 ISO 3166 alpha-2 country code,然后一个句点,然后最多 128 个字母数字字符,像 US.13491208655302741918。country code 和句点是值的一部分 [2],所以剥掉它们、或者像你整理电话号码那样去「归一化」BSUID,都会弄坏发送。

有一个真正的例外。Authentication 类别的模板仍然需要电话号码。One-tap、zero-tap 和 copy-code 这些 auth 模板不能发往一个 BSUID,尝试这样做会返回 Meta 错误码 131062。[6] 如果你的一次性密码流程跑在 WhatsApp 上,它就需要号码,这又多了一个理由,让你趁还有电话号码时就抓住 phone-to-BSUID 这一对。

身份模型:不透明、按作用域划分,且并不总能逆转

BSUID 的行为方式,决定了它在你数据模型里该放在哪儿。下面每一行都对应一个设计决策。

属性 行为
作用域 每个 business portfolio 唯一。同一个用户和每一家业务交互时拿到的 BSUID 都不一样,所以它不是一个全局的人物主键。[1][2]
不透明性 一个不透明 token。它不是电话号码,除了 country 前缀之外不携带任何可解析的含义。[2]
格式 {ISO 3166 alpha-2}.{最多 128 个字母数字},整体使用,包括 country code 和句点。[2]
username 变更 稳定。更改 username 不会改变 BSUID。[2]
电话号码变更 重新生成。更改电话号码会产生一个新的 BSUID,并且 Meta 会发一条 user_id_update webhook,同时携带旧的和新的 BSUID,好让你重新挂接。[1][2]

标识符变更时重新挂接,不要重复创建

当某人更改了他账户上的电话号码,BSUID 会被重新生成,而 Meta 通过一条已记录在案的 user_id_update webhook 来宣告这件事,该 webhook 同时携带旧的和新的 BSUID。[1][2] 用旧的 BSUID 去找到那条已有记录,用新的去更新它,这样这次变更就落成一次合并,而不是一个新的联系人。把它当成一个全新的用户来处理,你就会让一个人的历史被分裂在两条记录里。

有一点要弄对:这是它自己的标识符变更负载,不是一个投递状态,所以不要把重新挂接接到本对照表别处的 statuses[] 回调上。有些 BSP 文档还会为同一次变更暴露一条并行的 system message,所以在你拿它来开发之前,对着 Meta 的在线参考确认确切的负载形态。各处的行为是一样的:标识符变了,所以重新挂接,而不是重复创建。

电话号码恢复是只向前的

Meta 的 Contact Book 会自动存储 phone-to-BSUID 的映射。它由 Meta 托管、默认开启,并且作用域限定在 business portfolio。[2] 让人意外的那个坑是:它只记录它在 2026 年 4 月上线之后的交互,永远不会回填,对于一个从未分享过号码、全新的、只有 username 的联系人,它根本没什么可记录。[2] 所以你不能指望以后还能为一个只有 BSUID 的联系人恢复出电话号码。

这就引出一个简单的习惯:每当一条 webhook 确实包含电话号码时,就在那一刻存下 phone-to-BSUID 这一对。不要留到以后。

那个标准数据模型范式

每一个 CRM 和消息集成最后都会走到同一个形态,而它是一个建立在一条规则之上的小改动。BSUID 是主键;电话号码是别名。

  • 把 BSUID 存为每个 (business, contact) 的持久身份主键。把电话号码保留为同一条记录上一个补充性的、可空的别名。[2][5]
  • 在每一条入站消息上读取 BSUID 字段(user_idfrom_user_id,或你 BSP 的等价物)作为可靠的身份。不要再把 tofromwa_id 当作保证存在的电话号码来读。[2]
  • 只要电话号码在场就捕获 phone-to-BSUID 这一对,因为 Contact Book 不会替你回填它。[2]
  • 在双标识符上合并。如果你已经按电话号码有了一个联系人,而在一段活跃对话期间同一个人又来了一个新的 BSUID,把它们关联起来,而不是创建第二条记录。
  • 在标识符变更事件上,把已有的联系人重新挂到新的 BSUID 上。[2]

如果你的 schema 把 WhatsApp 联系人键在电话号码上,那就是第一件要修的事,因为电话号码现在是那个可能消失的字段,而 BSUID 才是那个总是到达的字段。

常见误解

这些在厂商博文和集成讨论串里已经在流传了。大多数都是那种听上去合理、能顺利通过 review、然后在第一个只有 username 的线索出现时就翻车的捷径。

BSUID 不过就是 wa_id 字段换了一种新值。 不是这样。它是一个独立的字段,contacts[].user_idmessages[].from_user_id,和电话字段并排坐着。[1] 那些盯着 wa_id 等「一个看起来不一样的值」的代码,在任何同时也有电话号码在场的负载上,会彻底错过 BSUID。

电话号码总是在那儿,所以把 from 当电话号码来解析是安全的。 不再是了。一旦某个用户采用了 username 并且和你没有近期历史,fromwa_id 就可能为空或不存在。[2] 任何把那些字段校验或格式化成 E.164 号码的逻辑,会恰好在你最想要的那些联系人,也就是新来的那些,上面崩掉。

你只要把 BSUID 丢进 to 字段,API 就会替你搞定。 那在某些 BSP 包装层上是对的,比如 Azure ACS [2],但在原始 Cloud API 上不是。直连 API 保留 to 给电话号码,并为 BSUID 发送增加一个独立的 recipient 字段,如果两者都在场则 to 优先。[1] 对着原始 API 把一个 BSUID 放进 to,你发出去的就不是你以为的那条。Authentication 模板是又一个例外:它们仍然需要电话号码,如果你把它们指向一个 BSUID 会返回错误码 131062。[6]

你可以剥掉 country 前缀和句点来「清理」BSUID。 你不能。country code 和句点是值的一部分,改动它的任何一部分都会让请求失败。[2]

BSUID 跨平台标识一个人。 它不会。它按 portfolio 划分作用域,所以同一个人在每一家业务那里都带着一个不同的 BSUID。你不能用它在两家业务之间认出某个人,也不能在不相关的 portfolio 之间共享身份。[1][2] 它是你世界内部的身份,不是跨平台的身份。

有了 Contact Book,就意味着你以后总能查到一个电话号码。 它只向前。它记录的是它在 2026 年 4 月上线之后的映射,永远不会回填,对于一个从未分享过号码、只有 username 的联系人,它根本没什么可记录。[2] 趁你还握着的时候,自己把这一对捕获下来。

一个电话号码变更事件意味着一个新用户。 那是同一个用户,BSUID 被重新生成了,所以重新挂接那条已有记录。[2] 反过来把它重复创建,你就会把一个人的历史分裂在两个联系人之间。

给在非官方协议上工作的团队的一点说明:BSUID 不是 @lid

如果你的技术栈也通过 Baileys 这类库碰到了非官方的多设备协议,你大概已经撞上过一个不同的标识符:当一个用户真实的电话号码 JID 被隐藏时出现的 @lid「LinkedID」JID。[8] 这些看起来是一回事,但它们不是。

BSUID 是一个官方的 Cloud API 构造,按 portfolio 划分作用域、不透明,在已记录在案的 webhook 字段里投递。@lid JID 活在 WhatsApp 的多设备寻址内部,是按用户全局的,不限定在某一家业务里。[8] 两者都在追同一个想法,把身份从电话号码上挪走,但它们是不同表面上的不同机制,你为其中一个建的映射不会平移到另一个上。

这个截止时间对 WhatsApp 的约束力,比对其他渠道更强

在 WhatsApp 上,这次身份变更落在一个本就在跟时钟赛跑的渠道之上。Meta 的 Business Platform 给你一个 24 小时客服窗:一旦用户给你发了消息,你有 24 小时可以用自由格式回复,之后你唯一的外发选项就是一条预先审批过的、付费的模板消息。[7] 一个只有 username 的线索发来的第一条消息,可能带着一个 BSUID、没有电话号码、而那个 24 小时计时器已经在倒数了。一个没法用 BSUID 认出、存下并回复这个联系人的集成,缺的不只是一个干净的数据模型。它可能错过那个免费回复窗,连同那条线索一起错过。

所以我们没有把 BSUID 就绪当作可选项,也没有等推出逼我们出手。StaffOS 已经在跑本文所主张的那个范式。我们把 WhatsApp 联系人键在那个持久的平台标识符上,把电话号码保留为一个可空的别名,只要电话号码在场就捕获 phone-to-BSUID 这一对,并在 user_id_update 事件上重新挂接而不是重复创建。对我们来说,一次只有 username 的首触从第一条消息起就是一个普通联系人,而不是一个要靠电话号码逻辑撑着的特殊情况。如果你正在两家 WhatsApp 厂商之间掂量,就把上面那份审计当作及格线:这项工作本该已经做完,而不是还躺在路线图上。

发货之前该核验什么

平台文档是真理之源,而随着面向用户的推出还在继续,它们也仍在被填补。在你针对某个具体负载写代码之前,对着 Meta 的在线参考核验三件事:user_id_update 标识符变更负载的确切形态、你的 BSP 递给你的是原始字段还是它自己重命名过的版本、以及自本文写就以来 contacts 对象里是否出现了任何新字段。上面那份对照表反映的是截至 2026 年 6 月的主文档和合作方文档,等 Meta 改了这个表面,我们会更新它。

如果这里有什么和在线文档对不上了,告诉我们,我们会修。整件事的关键就是它要一直保持可核验。

Frequently asked questions

什么是 WhatsApp Business-Scoped User ID(BSUID)? +

BSUID 是 Meta 在单一 business portfolio 内为某个 WhatsApp 用户分配的一个不透明、稳定的标识符。它的作用域是按业务划分的:同一个人和每一家业务对话时拿到的 BSUID 都不一样,所以它并不是一个跨业务通用的人物主键。它的 wire 格式是一个 ISO 3166 alpha-2 country code,加一个句点,再加上最多 128 个字母数字字符,例如 US.13491208655302741918。country code 和句点是值的一部分,绝不能剥掉。

BSUID 在 WhatsApp Cloud API 的 webhook 里出现在哪里? +

在直连的 Cloud API 上,BSUID 是一个新的、独立的字段,而不是塞进 wa_id 里复用。它在每一条 message webhook 里都以 contacts[].user_id 和 messages[].from_user_id 出现,无论用户是否采用了 username。电话字段 contacts[].wa_id 和 messages[].from 在电话号码可用时仍然携带它,只有当用户采用了 username、且 Meta 的电话号码包含条件不满足时才会被省略。username(如果设置了)位于 contacts[].profile.username。

给一个 BSUID 发消息,我需要新的端点或字段吗? +

端点不变,但在原始 Cloud API 上字段确实变了。电话号码发送仍然用 to。BSUID 发送用一个新的收件人字段。两者至少要提供其一,如果两者都有,Meta 让 to 优先,所以把它们当成两个独立的寻址输入,而不是一个自动判别的字段。有些 BSP 包装层把这一点藏起来了;比如 Azure Communication Services 在它单一的 to 数组里既接受电话号码也接受 BSUID,但那是 BSP 的归一化处理,不是直连契约本身。Authentication 类别的模板(one-tap、zero-tap、copy-code)仍然需要电话号码,把这类模板发往一个 BSUID 会返回 Meta 错误码 131062。

一家业务能不能总是把一个 BSUID 背后的电话号码找回来? +

不能。Meta 的 Contact Book 会自动存储 phone-to-BSUID 的映射,但只针对它在 2026 年 4 月上线之后发生的交互。它不会回填之前的交互,而一个全新的、只有 username 的联系人根本就没有电话号码可映射。实际含义是:只要电话号码在场,就立刻把 phone-to-BSUID 这一对捕获进你自己的存储,而不要假设你以后还能解析出来。

BSUID 和开发者在非官方库里看到的 @lid 标识符是一回事吗? +

不是。BSUID 是一个官方的 WhatsApp Cloud API 构造,按 portfolio 划分作用域,且不透明。@lid LinkedID JID 是一个协议层的标识符,由 Baileys 这类非官方多设备库暴露出来,它是按用户全局的,而不是限定在某一家业务内。它们在哲学目标上是一致的,都是要把身份和电话号码解耦,但它们是不同表面上的不同机制,不应混为一谈。

当用户更换电话号码时,BSUID 会怎样? +

BSUID 在 username 变更时保持稳定,但在用户更换电话号码时会被重新生成。Meta 通过它已记录在案的 user_id_update webhook 来呈现这一变更,该 webhook 同时携带旧的和新的 BSUID,因此集成方可以把已有的联系人重新挂到新的标识符上,而不是创建一个重复记录。这是它自己的标识符变更负载,独立于 statuses[] 投递回调,所以正确的响应是合并,而不是新建联系人。

2026 年,一个 CRM 应该把什么存为 WhatsApp 联系人的身份主键? +

把 BSUID 存为每个 (business, contact) 的持久身份主键,并把电话号码保留为一个补充性的、可空的别名。在每一条入站消息上读取 BSUID 字段作为可靠的身份,不要再假设 to、from 或 wa_id 字段携带电话号码,只要电话号码在场就捕获 phone-to-BSUID 这一对,并在标识符变更事件上重新挂接,而不是重复创建。

References

  1. [1] Meta for Developers, Business-scoped user IDs (WhatsApp Business Platform, primary documentation)
  2. [2] Microsoft Azure Communication Services, WhatsApp usernames and business-scoped user IDs (BSUID) overview
  3. [3] Twilio Changelog, WhatsApp usernames: new Business-Scoped User ID (BSUID) field
  4. [4] Infobip Docs, WhatsApp usernames and user IDs
  5. [5] 360dialog, WhatsApp usernames: business impact 2026
  6. [6] YCloud, WhatsApp usernames and business-scoped user IDs explained (2026 guide)
  7. [7] Meta for Developers, WhatsApp Business Platform pricing (24-hour customer-service window)
  8. [8] Baileys, migration to v7.0.0 (LID / LinkedID addressing in the multi-device protocol)

About the author

Same Tham

联合创始人 / 首席营销官,StaffOS

Same 负责 StaffOS 的市场与增长。StaffOS 是一个 AI 员工平台,为东南亚的小企业运营客服与潜在客户筛选。

Related reading