WhatsApp 联系信息请求
联系信息请求在 WhatsApp 消息下方放置一个按钮,请求收件人分享电话号码。当你需要一个可以联系到对方的号码(例如回拨或预订确认),而非一个已保存的地址时,使用它。如果需要位置信息,请使用位置请求。
发送联系信息请求
将 interactive.type 设为 request_contact_info,附带一个 body_text,不含其他内容。WhatsApp 会渲染按钮本身,因此没有可供标注按钮的字段:
const msg = await bird.whatsapp.send({
to: "+16505551234",
from: "+13124495648",
interactive: {
type: "request_contact_info",
body_text:
"To confirm your booking we need a number to reach you on. Tap below to share yours.",
},
});
console.log(msg.id, msg.status);msg = client.whatsapp.send(
to="+16505551234",
from_="+13124495648",
interactive={
"type": "request_contact_info",
"body_text": "To confirm your booking we need a number to reach you on. Tap below to share yours.",
},
)
print(msg.id, msg.status)package main
import (
"context"
"fmt"
"log"
"os"
bird "github.com/messagebird/bird-sdk-go"
"github.com/messagebird/bird-sdk-go/option"
)
func main() {
client, err := bird.NewClient(option.WithAPIKey(os.Getenv("BIRD_API_KEY")))
if err != nil {
log.Fatal(err)
}
msg, err := client.Whatsapp.Send(context.Background(), bird.WhatsappSendParams{
To: "+16505551234",
From: "+13124495648",
Interactive: &bird.WhatsAppInteractiveSend{
Type: "request_contact_info",
BodyText: "To confirm your booking we need a number to reach you on. Tap below to share yours.",
},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(msg.Id, *msg.Status)
}$interactive = (new WhatsAppMessageSendRequestInteractive())
->setType('request_contact_info')
->setBodyText('To confirm your booking we need a number to reach you on. Tap below to share yours.');
$message = $bird->whatsapp->send(
to: '+16505551234',
from: '+13124495648',
interactive: $interactive,
);
echo $message->getId(), ' ', $message->getStatus();bird whatsapp send \
--from +13124495648 \
--interactive '{"body_text":"To confirm your booking we need a number to reach you on. Tap below to share yours.","type":"request_contact_info"}' \
--to +16505551234{
"name": "whatsapp_send",
"arguments": {
"from": "+13124495648",
"interactive": {
"body_text": "To confirm your booking we need a number to reach you on. Tap below to share yours.",
"type": "request_contact_info"
},
"to": "+16505551234"
}
}curl -X POST "https://{region}.platform.bird.com/v1/whatsapp/messages" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"to": "+16505551234",
"from": "+13124495648",
"interactive": {
"type": "request_contact_info",
"body_text": "To confirm your booking we need a number to reach you on. Tap below to share yours."
}
}'from 在每条服务消息中都是必填项:必须是你的工作区拥有的号码,而非 Bird 托管的号码。此类型没有自己的专属字段,且 schema 明确禁止 header、footer_text 以及所有其他类型的字段(buttons、list、cta_url、cards),因此 body_text 就是整条消息,上限为 1024 个字符。Meta 没有为此类型规定正文长度限制;Bird 对除列表菜单之外的所有其他交互类型施加了 1024 字符的限制。
in_reply_to_message_id 在此类型上仍然可用,用于引用同一对话中的早期消息。请参阅 hub 的引用消息以关联回复,了解解析方式及其可能遗漏的情况。
读取共享的联系人
点击不会产生 interactive_reply。它以普通入站消息的形式到达,携带一个 contact_cards 数组:
代码示例
{
"id": "wam_01kyb2m4xq7whs0d8n3prv6tez",
"direction": "inbound",
"from": { "phone_number": "+16505551234", "bsuid": "US.13491208655302741918" },
"to": { "phone_number": "+13124495648" },
"status": "received",
"contact_cards": [
{
"origin": "contact_request",
"phone_numbers": [{ "phone_number": "+14155550829", "type": "cell" }]
}
],
"created_at": "2026-08-26T10:00:00Z"
}contact_cards 是一个数组,未携带卡片的 contacts 消息读回时为 [],而非字段缺失。同一字段也承载你发送的卡片,因此用于回答此请求的卡片通过 origin 来区分,而非通过它所在的字段。在将卡片视为你的回答之前,检查 origin 是必须的。 当卡片回答了此请求时,origin 为 contact_request;当联系人主动分享卡片时为 other,此时卡片可能完全指向第三方,而非该联系人本人。点击仅携带 phone_numbers[].{phone_number, type} 并省略 vcard;完整的联系人对象(包含 name、org、birthday 等)仅在 origin: "other" 上到达。你可以通过消息列表或 GET /v1/whatsapp/messages/{id} 查看此回复;请参阅 hub 的读取回复获取完整路径。
将回答与问题关联
与位置请求不同,Meta 不会在此类型的回复上附加 context,因此 in_reply_to_message_id 被省略而非被解析。通过 from 加上你近期的一次发送来关联,或者接受无法关联的事实。向同一联系人发出的两个未完成请求无法区分: 回复中没有任何内容能标识它回答的是哪个请求,因此如果工作区在第一个请求被回答之前发送了第二个联系信息请求,就无法判断哪张卡片对应哪个请求。
这是与位置请求的刻意区别:位置请求的回复携带 Meta 自己的 context,因此 in_reply_to_message_id 可以解析,hub 的引用消息以关联回复机制会自动将回答链接回去。联系信息请求的回复没有这样的机制可依赖。
改用模板发送请求
request_contact_info 交互消息是 REQUEST_CONTACT_INFO 模板按钮的自由格式对应物,它请求相同的联系人卡片,但可以在收件人的客户服务窗口关闭时送达。当收件人最近向你发过消息且你希望按本次对话的措辞来提问时,使用交互消息;当窗口已关闭,或该请求附在你已作为模板发送的消息上时,使用模板按钮。请参阅 WhatsApp 模板了解如何通过模板发送。
注意事项
- 客户服务窗口必须处于打开状态。 联系信息请求是服务消息,仅在窗口打开时可投递;请参阅 hub 的客户服务窗口。窗口检查以开放方式失败,因此 202 并不能证明发送时窗口确实处于打开状态。
- from 必须是你的工作区拥有的号码,且其所需的打开窗口与该号码绑定,而非与整个工作区绑定。
- 回复无法通过 id 与请求关联。 Meta 端没有 context 意味着回复上的 in_reply_to_message_id 被省略;通过 from 加上你近期的一次发送来关联。
- 拒绝是静默的。 WhatsApp 会向收件人展示一个分享面板,关闭它不会产生任何消息或 webhook。contact_cards 消息的缺失是唯一的信号,因此任何等待回答的流程都需要自行设置超时,而非等待拒绝事件。
- 没有请求头、没有页脚、没有按钮标签。 schema 明确禁止此类型上的 header 和 footer_text,也没有字段可供标注按钮。收件人看到的所有内容都必须在 body_text 中。
- 分享的号码不一定是联系人聊天时使用的号码。 Meta 警告用户的 ID 和电话号码可能并不总是匹配,因此不要假设分享的号码等于 from.phone_number。该号码也不保证是 E.164 格式:Bird 在可解析时进行规范化,无法解析时原样传递。
- 回复是 contact_cards 消息,而非 interactive_reply。 仅监听 interactive_reply 以捕获点击的集成将完全错过此类型,仅监听入站 location 以捕获另一种请求类型的集成也是如此。
schema 能在此处表达的所有错误(过长的 body_text、header、footer_text,或任何 buttons、list、cta_url、cards)都是普通的请求验证失败,没有目录错误码。无法解析的引用会在创建或计费之前使请求失败:当 id 指定的消息不属于此工作区时返回 404 E15071,当 id 指定的消息无法被引用时返回 422 E15072。请参阅 hub 的错误获取完整的交互错误表,以及发送 WhatsApp 消息了解任何 WhatsApp 发送可能遇到的错误。
后续步骤
- WhatsApp 交互消息:六种交互类型的共同点
- 位置请求:请求位置信息而非电话号码
- WhatsApp 模板:在收件人的客户服务窗口关闭时送达
- 发送 WhatsApp 消息:请求信封、202 模型与安全重试