接收 WhatsApp 联系人名片
contact_cards 是唯一一个在收发两个方向上使用同一字段的分支。联系人可以从通讯录中分享一张名片,而对方点击你发送的联系信息请求后的结果也会到达这里,携带他们选择提供的号码。
入站联系人名片包含的内容
contact_cards 始终是一个数组,origin 表示名片的来源方式:
代码示例
{
"id": "wam_01kyg9v3timy1w7n0r4cbh9ukf",
"direction": "inbound",
"from": { "phone_number": "+14155550100" },
"to": { "phone_number": "+13124495569" },
"status": "received",
"contact_cards": [
{
"origin": "contact_request",
"phone_numbers": [{ "phone_number": "+14155550100", "type": "cell" }]
}
],
"created_at": "2026-08-25T09:27:45Z"
}| origin | 名片的来源方式 |
|---|---|
| contact_request | 联系人点击了你发送的索要号码的按钮 |
| other | 联系人在聊天中主动分享了一张名片 |
在将名片视为你请求的回复之前,请先检查 origin。 这是区分两种来源的唯一信号,主动分享的名片可能完全是第三方的信息而非联系人本人的。该值列表是开放的,遇到未识别的值时,将其视为此后新增的另一种分享方式即可。
该工作区发送的名片在回读时不带任何 origin,这就是在同一字段上区分出站名片和入站名片的方式。
点击携带的内容与分享名片携带的内容
两者到达时携带的详细程度不同,且名片上没有任何字段是必填的:WhatsApp 发送名片中包含的部分并省略其余部分,因此即使名片只包含一个 origin,它仍然会送达而不会被丢弃。
| 字段 | 按钮点击时 | 在聊天中分享名片时 |
|---|---|---|
| phone_numbers[].phone_number、type | 联系人选择提供的号码 | 名片中包含的所有号码 |
| vcard | 省略;点击只携带号码 | vCard 格式的名片 |
| name、org、birthday、emails、urls、addresses | WhatsApp 发送的内容,通常为空 | 名片包含这些字段时才会出现 |
代码示例
{
"contact_cards": [
{
"origin": "other",
"vcard": "BEGIN:VCARD\nVERSION:3.0\nN:Johnson;Barbara;;;\nTEL;type=CELL:+16505551234\nEND:VCARD\n",
"name": {
"formatted_name": "Barbara J. Johnson",
"first_name": "Barbara",
"last_name": "Johnson"
},
"org": { "company": "Northside Plumbing" },
"phone_numbers": [{ "phone_number": "+16505551234", "type": "cell" }]
}
]
}解析时有两个字段需要注意。phone_number 在可解析时会被规范化为 E.164 格式,无法解析时则原样透传联系人设备上存储的值(分机号即属此类),因此请防御性地解析,不要假定一定是 E.164。birthday 来自联系人设备,未经验证,以 YYYY-MM-DD 形式的文本透传而非日期类型,因此不要假定它可以被解析。接收到的名片上的 type 标签会被转为小写,且 WhatsApp 未定义其词汇表,因此请以不区分大小写的方式匹配,而不要直接对 CELL 进行精确匹配。
联系人提供的电话号码
已设置 WhatsApp 用户名的联系人通过业务范围内的用户 ID与你通信,from 上没有电话号码。联系信息请求是你索要号码的方式,而这个分支就是回复到达的地方,携带 origin: "contact_request" 和 phone_numbers 中的号码。
对方提供的号码不一定是其聊天所用的号码:Meta 警告用户的标识符和电话号码可能并不总是一致,因此请将提供的号码作为独立信息存储,而不要覆盖 from 上的身份信息。
Webhook 载荷
whatsapp.received 在事件信封上携带 contact_cards 数组:
代码示例
{
"type": "whatsapp.received",
"timestamp": "2026-08-25T09:27:45.019Z",
"data": {
"whatsapp_id": "wam_01kyg9v3timy1w7n0r4cbh9ukf",
"workspace_id": "ws_01ky7m235keycbnwyajabe1a6b",
"direction": "inbound",
"from": { "phone_number": "+14155550100", "bsuid": "US.13491208655302741918" },
"to": { "phone_number": "+13124495569" },
"contact_cards": [
{
"origin": "contact_request",
"phone_numbers": [{ "phone_number": "+14155550100", "type": "cell" }]
}
],
"tags": null,
"metadata": null
}
}注意事项
- 拒绝的请求不会产生任何内容。 WhatsApp 会向联系人显示一个分享面板,关闭该面板不会发送消息也不会触发 webhook,因此等待号码的流程需要设置自己的超时,而不是等待一个拒绝事件。
- 两个未完成的请求无法区分。 回复联系信息请求的名片不携带 in_reply_to_message_id,因此在第一个请求被回复之前发送的第二个请求无法与其自己的回复匹配。
- 数组可以包含多张名片。 联系人在一条消息中分享多张名片时,会填充多个条目,每个条目都有自己的 origin。
- 名片是你未主动收集的联系人数据。 它可能包含第三方的姓名、号码和生日,因此在存储之前,请对其适用与其他个人数据相同的保留和同意规则。
后续步骤
- 接收的工作方式:入站信封、媒体获取和 whatsapp.received webhook
- WhatsApp 联系人名片:同一分支的发送端
- 业务范围内的用户 ID:联系人为何没有电话号码,以及请求如何融入对话
- WhatsApp 联系信息请求:用于索要号码的按钮