WhatsApp 联系人卡片
联系人卡片消息可分享一个或多个联系人:收件人在卡片上看到的姓名,以及从卡片打开的个人资料视图,其中包含电话号码、电子邮件、网站、地址、雇主和生日。用它把同事的号码、快递员的号码或您自己的号码递给客户,无需将数字粘贴到文本中让对方重新手动输入。
发送联系人卡片
contact_cards 是一个数组。每张卡片需要一个 name,且该名称需要 formatted_name 加上至少一个其他部分:
const msg = await bird.whatsapp.send({
to: "+16505551234",
from: "+13124495648",
contact_cards: [
{
name: {
formatted_name: "Barbara J. Johnson",
first_name: "Barbara",
last_name: "Johnson",
},
phone_numbers: [{ phone_number: "+16505559999", type: "Mobile" }],
},
],
});
console.log(msg.id, msg.status);msg = client.whatsapp.send(
to="+16505551234",
from_="+13124495648",
contact_cards=[
{
"name": {
"formatted_name": "Barbara J. Johnson",
"first_name": "Barbara",
"last_name": "Johnson",
},
"phone_numbers": [{"phone_number": "+16505559999", "type": "Mobile"}],
}
],
)
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",
ContactCards: []bird.WhatsAppContactCardSend{{
Name: bird.WhatsAppContactNameSend{
FormattedName: "Barbara J. Johnson",
FirstName: bird.String("Barbara"),
LastName: bird.String("Johnson"),
},
PhoneNumbers: &[]bird.WhatsAppContactPhoneSend{{
PhoneNumber: "+16505559999",
Type: bird.String("Mobile"),
}},
}},
})
if err != nil {
log.Fatal(err)
}
fmt.Println(msg.Id, *msg.Status)
}$name = (new WhatsAppContactCardSendName())
->setFormattedName('Barbara J. Johnson')
->setFirstName('Barbara')
->setLastName('Johnson');
$phone = (new WhatsAppContactPhoneSend())
->setPhoneNumber('+16505559999')
->setType('Mobile');
$card = (new WhatsAppContactCardSend())
->setName($name)
->setPhoneNumbers([$phone]);
$message = $bird->whatsapp->send(
to: '+16505551234',
from: '+13124495648',
contactCards: [$card],
);
echo $message->getId(), ' ', $message->getStatus();bird whatsapp send \
--to +16505551234 \
--from +13124495648 \
--contact-cards '[{"name":{"formatted_name":"Barbara J. Johnson","first_name":"Barbara","last_name":"Johnson"},"phone_numbers":[{"phone_number":"+16505559999","type":"Mobile"}]}]'curl -X POST "https://us1.platform.bird.com/v1/whatsapp/messages" \
-H "Authorization: Bearer $BIRD_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"to": "+16505551234",
"from": "+13124495648",
"contact_cards": [
{
"name": {
"formatted_name": "Barbara J. Johnson",
"first_name": "Barbara",
"last_name": "Johnson"
},
"phone_numbers": [
{ "phone_number": "+16505559999", "type": "Mobile" }
]
}
]
}'from 在每条服务消息中都是必填项:一个您的工作区拥有的号码,而非 Bird 托管的号码。
完整结构还包含雇主、生日以及其他联系方式详情数组:
代码示例
{
"to": "+16505551234",
"from": "+13124495648",
"contact_cards": [
{
"name": {
"formatted_name": "Dr. Barbara J. Johnson Esq.",
"prefix": "Dr.",
"first_name": "Barbara",
"middle_name": "Joana",
"last_name": "Johnson",
"suffix": "Esq."
},
"org": { "company": "Lucky Shrub", "department": "Legal", "title": "Lead Counsel" },
"birthday": "1999-01-23",
"phone_numbers": [
{ "phone_number": "+16505559999", "type": "Landline" },
{ "phone_number": "+19175559999", "type": "Mobile" }
],
"emails": [{ "email": "bjohnson@example.com", "type": "Work" }],
"urls": [{ "url": "https://example.com", "type": "Company" }],
"addresses": [
{
"street": "1 Lucky Shrub Way",
"city": "Menlo Park",
"state": "CA",
"zip": "94025",
"country": "United States",
"country_code": "US",
"type": "Office"
}
]
}
]
}每个 type 标签,无论用在电话、电子邮件、网站还是地址上,都是您自行填写的自由文本,按原样发送,并在收件人的个人资料视图中显示在值的旁边。WhatsApp 未定义这些标签的词汇表,因此 Mobile、Landline、Pop-Up 和 Work (old) 都同样有效。
卡片获得按钮的条件
以 E.164 格式书写的电话号码(包含国家代码和前导 +)会为该卡片赢得一个按钮,点击即可与该号码打开 WhatsApp 聊天。Bird 无法识别为 E.164 的号码仍会按原样显示在卡片上,只是不会获得按钮。
这包括没有前导 + 的号码。Bird 不会为您补上:一个国内格式的号码在加上 + 后,可能被解析为另一个国家的有效号码,从而将按钮指向一个陌生人。放弃猜测只是少一个按钮;猜错则让收件人与错误的人聊天。
完全不包含电话号码的卡片不会显示聊天按钮,只能被保存到通讯录。
限制
| 字段 | 约束 | 执行方 |
|---|---|---|
| contact_cards | 每条消息 1 到 5 张卡片 | Bird,在接受时(422) |
| name | 必填;formatted_name 加上至少一个其他名称部分 | Bird,在接受时(422) |
| formatted_name、first_name、middle_name、last_name | 最多 256 个字符 | Bird,在接受时(422) |
| prefix、suffix | 最多 64 个字符 | Bird,在接受时(422) |
| birthday | 可选,YYYY-MM-DD,且为日历中存在的日期 | Bird,在接受时(422) |
| phone_numbers、emails、urls、addresses | 各最多 10 个条目 | Bird,在接受时(422) |
| phone_number | 最多 32 个字符 | Bird,在接受时(422) |
| 最多 254 个字符 | Bird,在接受时(422) | |
| url | 最多 2048 个字符,不校验是否为 URL | Bird,在接受时(422) |
| 任意电话、电子邮件、网站或地址上的 type | 最多 64 个字符的自由文本 | Bird,在接受时(422) |
| company、department、title | 最多 128 个字符 | Bird,在接受时(422) |
| street、city、state、zip、country、country_code | 最多 128 个字符 | Bird,在接受时(422) |
五张卡片的上限是 Bird 设定的,且远低于 WhatsApp 所接受的数量。 WhatsApp 自己发布的 API 描述声明上限为五,其文档建议出于可用性和负面反馈原因发送更少,而一打开就是 "Contact 1 and 256 other contacts" 的消息在成为功能之前先是垃圾信息载体。日后提高上限属于增量变更,因此如果五张不够用,请提出需求。
上面的所有长度限制也是 Bird 设定的。WhatsApp 没有施加任何有意义的限制,其客户端也不会补偿:500 个字符的 type 会渲染成十行重复字母,4000 个字符的 url 会被静默丢弃,使个人资料视图留白。一个指出问题字段的 422 胜过一张收件人无法阅读的卡片。
Schema 无法表达的两条规则
姓名需要第二个部分。 仅有 formatted_name 会被拒绝,返回 422 E15061 WhatsAppContactNameIncomplete,指明 contact_cards.<n>.name。prefix、first_name、middle_name、last_name 或 suffix 中的任意一个即可满足要求,但空白或仅含空格的值不算,org 也无法弥补。这是 WhatsApp 自身的要求,其参考文档中未有记载;Bird 会在接受时捕获此错误,让您获得可操作的错误而非异步失败。
生日必须是真实日期。 birthday 的格式为 YYYY-MM-DD;任何其他格式以及日历中不存在的日期(例如 2026-02-30)都会被拒绝,返回 422 E15062 WhatsAppContactBirthdayInvalid。WhatsApp 本身接受 2026-02-30 并将其展示给收件人,看起来像是您数据中的 bug。
回读卡片
您发送的卡片通过消息列表或 GET /v1/whatsapp/messages/{id} 在入站卡片使用的同一个 contact_cards 字段上回读:
代码示例
{
"id": "wam_01kyb2m4xq7whs0d8n3prv6tez",
"direction": "outbound",
"status": "delivered",
"contact_cards": [
{
"name": { "formatted_name": "Barbara J. Johnson", "first_name": "Barbara" },
"phone_numbers": [{ "phone_number": "+16505559999", "type": "Mobile" }]
}
]
}您发送的卡片上不存在 origin 和 vcard:WhatsApp 在联系人分享的卡片上设置这两个字段。您发送的 type 标签回读时与原文完全一致,而收到的卡片上的标签会被转为小写。入站侧详见接收 WhatsApp 联系人卡片。
边界情况
- 客服窗口必须处于打开状态。 联系人卡片发送属于服务消息,仅在打开的窗口内可投递;请参阅 Hub 的客服窗口。
- 没有可发送的 wa_id。 WhatsApp 通过帐户 ID 标识卡片的联系人;Bird 从每个 E.164 phone_number 中推导该 ID,而非接受一个现成的 ID,因此卡片上的按钮永远只会指向卡片上印刷的号码。
- vcard 是只读的。 WhatsApp 在联系人分享的卡片上生成该字段。无法以原始 vCard 文本的形式发送卡片。
- 卡片不是联系人记录。 发送卡片只是在消息中分享详情;它不会在您的工作区中创建任何内容,收件人是否保存完全是他们自己的操作,对您不可见。
后续步骤
- WhatsApp 服务消息:客服窗口以及所有服务消息共享的模型
- 联系信息请求:向联系人索要号码,而非发送号码
- 接收 WhatsApp 消息:入站消息、媒体和 whatsapp.received webhook
- 发送 WhatsApp 消息:请求信封、202 模型和安全重试