关于 contacts-management
contacts-management 是用于 Zavu 联系人 API 的技能,面向构建联系人及其沟通渠道管理功能的开发者。它涵盖多渠道联系人模型、搜索与合并,以及电话号码和电子邮件验证。
主要功能
- 01管理多渠道联系人信息
- 02按姓名、标识符和标签搜索
- 03添加或移除联系人渠道
- 04合并联系人及其渠道
- 05检查电话号码与运营商信息
- 06验证电子邮件地址
适用场景
- 01创建和更新联系人记录
- 02按渠道标识符查找联系人
- 03合并同一人的重复联系人
- 04发送前筛查电话号码和邮件地址
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ zavudev/ zavu-skills --skill contacts-management安装后在 Claude Code 中输入/contacts-management确认已加载。
详细介绍
联系人管理
适用场景
在编写用于创建、更新或管理联系人及其通信渠道的代码时,使用此技能。涵盖多渠道联系人模型、搜索、合并操作、电话号码信息查询和电子邮件地址验证。
联系人模型
联系人支持多个渠道:一个联系人可以拥有多个渠道(SMS、WhatsApp、Email、Telegram、Instagram、Messenger、Voice),每个渠道都有自己的标识符和投递指标。顶层字段会提供主要标识符,方便快速访问。
Contact (John Doe)
├── primaryPhone: +14155551234 (E.164)
├── primaryEmail: [email protected]
├── profileName: "John D." (WhatsApp profile name, if available)
├── verified: true
├── countryCode: "US"
└── Channels:
├── SMS: +14155551234 (primary)
├── WhatsApp: +14155551234 (primary)
├── Email: [email protected] (primary)
├── Email: [email protected] (label: "work")
├── Telegram: @johndoe
└── Voice: +14155551234
注意: Contact 上的旧版
phoneNumber字段已弃用,请改用primaryPhone。
有效的联系人渠道类型:sms、whatsapp、email、telegram、instagram、messenger、voice(注意:auto 和 sms_oneway 是消息发送路由选项,不是联系人渠道类型)。
自动创建
向新收件人发送消息时,系统会自动创建联系人。基本消息发送无需显式创建联系人。
创建联系人
const contact = await zavu.contacts.create({
displayName: "John Doe",
channels: [
{ channel: "sms", identifier: "+14155551234", isPrimary: true },
{ channel: "whatsapp", identifier: "+14155551234", isPrimary: true },
{ channel: "email", identifier: "[email protected]", isPrimary: true },
],
metadata: { source: "import", plan: "enterprise" },
});
console.log(contact.id);
如果某个标识符已被另一联系人用于同一渠道,系统会拒绝该请求,
并返回 400(This sms identifier is already associated with another contact)。
请将该渠道添加到现有联系人,或合并联系人。
Python:
contact = zavu.contacts.create(
display_name="John Doe",
channels=[
{"channel": "sms", "identifier": "+14155551234", "isPrimary": True},
{"channel": "email", "identifier": "[email protected]", "isPrimary": True},
],
)
Go:
contact, err := client.Contacts.Create(context.TODO(), zavudev.ContactCreateParams{
DisplayName: zavudev.String("John Doe"),
Channels: []zavudev.ContactChannelParam{
{Channel: "sms", Identifier: "+14155551234", IsPrimary: zavudev.Bool(true)},
{Channel: "email", Identifier: "[email protected]", IsPrimary: zavudev.Bool(true)},
},
})
Ruby:
contact = client.contacts.create(
display_name: "John Doe",
channels: [
{ channel: "sms", identifier: "+14155551234", is_primary: true },
{ channel: "email", identifier: "[email protected]", is_primary: true },
],
)
PHP:
$contact = $client->contacts->create([
'displayName' => 'John Doe',
'channels' => [
['channel' => 'sms', 'identifier' => '+14155551234', 'isPrimary' => true],
['channel' => 'email', 'identifier' => '[email protected]', 'isPrimary' => true],
],
]);
获取和列出联系人
// Get by ID
const contact = await zavu.contacts.get({ contactId: "ct_abc123" });
// Get by phone number
const contact = await zavu.contacts.getByPhone({
phoneNumber: "+14155551234",
});
// List with filters
let cursor: string | undefined;
do {
const result = await zavu.contacts.list({
phoneNumber: "+14155551234",
limit: 50,
cursor,
});
for (const contact of result.items) {
console.log(contact.id, contact.displayName, contact.availableChannels);
}
cursor = result.nextCursor ?? undefined;
} while (cursor);
// Search by name, phone, or email
const found = await zavu.contacts.list({ search: "marta", limit: 25 });
search 不区分大小写和重音符号,并且支持匹配电话号码的末尾片段,因此 5551234 可以找到 +14155551234。它会匹配联系人的 displayName、WhatsApp 个人资料名称以及标识符。
通过收到的入站消息自动创建的联系人没有 displayName,因此在命名之前只能通过标识符查找——请参阅更新联系人。
搜索分页前请注意两点:结果按相关性排序,而非按最新顺序排序;两种模式下的 cursor 都是不透明的。请原样传回上一次响应中的值,并在搜索词发生变化时重新开始分页。
WhatsApp 用户名联系人(BSUID)
采用了用户名的 WhatsApp 联系人可以通过其企业专属用户 ID(BSUID)识别,而非电话号码,或与电话号码同时使用。如果 Zavu 有该值,联系人会包含 whatsapp: { bsuid, username? };否则该键不存在。使用精确的 bsuid 筛选条件查找联系人(请使用 REST,SDK 可能尚未提供此功能):
curl "https://api.zavu.dev/v1/contacts?bsuid=CO.13491208655302741918" \
-H "Authorization: Bearer $ZAVUDEV_API_KEY"
此操作会返回持有该 BSUID 的联系人,或返回空列表;还可以使用 phoneNumber 和 tag 进一步缩小范围。如果与非空的 search 一起使用,或值的格式不像 BSUID,则会返回 400(空白的 bsuid 会被忽略)。之后关联电话号码时,同一联系人会保留其 id;但如果关联操作发现同一人对应另一个联系人,且两者随后被合并,则 id 会发生变化。请订阅 contact.identity_linked,其中的 mergedContactId 会标明被移除的联系人 ID。请参阅 webhook-setup 技能。
标签
联系人上的扁平标签,用于对需要重复发送消息的受众进行分组。 支持多对多关系,没有层级或颜色。
// Contacts carrying a tag
const { items } = await zavu.contacts.list({ tag: "vip" });
// Repeat the parameter for AND — carrying BOTH tags, not either
// GET /v1/contacts?tag=vip&tag=chile
标签按名称匹配,不区分大小写。未知标签会返回 400,
而不是被忽略:拼写错误却悄悄匹配到所有联系人的结果,比返回错误更糟。
按标签筛选的页面返回结果可能少于 limit,甚至为空,但
nextCursor 仍可能有值。请继续分页,直到 nextCursor 为 null。
创建、重命名和分配标签需在控制面板中完成。目前 API 尚不支持标签 CRUD——不要告诉用户可以通过代码创建标签。
渠道操作
// Add channel
const channel = await zavu.contacts.channels.add({
contactId: "ct_abc123",
channel: "email",
identifier: "[email protected]",
label: "work", // optional
countryCode: "US", // optional, 2-letter ISO
});
// Update channel
await zavu.contacts.channels.update({
contactId: "ct_abc123",
channelId: "ch_xyz789",
label: "personal",
verified: true,
});
// Set as primary
await zavu.contacts.channels.setPrimary({
contactId: "ct_abc123",
channelId: "ch_xyz789",
});
// Remove channel (cannot remove the last channel)
await zavu.contacts.channels.remove({
contactId: "ct_abc123",
channelId: "ch_xyz789",
});
更新联系人
可更新的字段:displayName、defaultChannel、metadata。
await zavu.contacts.update({
contactId: "ct_abc123",
displayName: "John Doe",
defaultChannel: "whatsapp",
metadata: { plan: "premium", region: "US" },
});
// Clear a field by passing null
await zavu.contacts.update({
contactId: "ct_abc123",
displayName: null, // falls back to the contact's identifier
defaultChannel: null,
});
从入站消息自动创建的联系人没有 displayName —
在设置名称之前,他们会显示为电话号码或电子邮件地址。为联系人命名后,就可以通过 contacts.list({ search }) 按名称查找。
若要更改联系人的电话号码或电子邮件地址,请添加或移除渠道 (参见 渠道操作),而不是更新联系人。
合并联系人
将一个联系人合并到另一个联系人。源联系人的所有渠道都会转移到目标联系人, 目标联系人的主要电话和电子邮件地址会重新计算,源联系人会标记为已合并,并不再出现在列表中。
const merged = await zavu.contacts.merge({
contactId: "ct_target123", // survives
sourceContactId: "ct_source456", // absorbed
});
console.log("Merged channels:", merged.channels.length);
合并无法撤销 — 请先确认这两个联系人属于同一个人。
Zavu 不会为你检测重复项。 不会提供合并建议:请根据自己的数据决定要合并哪些联系人。一个实用的候选查找方法是 列出联系人,并按共同标识符或规范化后的名称分组。
电话信息查询
验证电话号码并查看运营商信息:
const result = await zavu.introspect.phone({
phoneNumber: "+14155551234",
});
console.log(result.validNumber); // true
console.log(result.countryCode); // "US"
console.log(result.nationalFormat); // "(415) 555-1234"
console.log(result.lineType); // "mobile" | "landline" | "voip" | "toll_free"
console.log(result.carrier?.name); // "Verizon Wireless"
console.log(result.availableChannels); // ["sms", "whatsapp", "voice"]
Python:
result = zavu.introspect.phone(phone_number="+14155551234")
print(result.valid_number)
print(result.line_type)
print(result.carrier.name if result.carrier else "Unknown")
Go:
result, err := client.Introspect.Phone(context.TODO(), zavudev.PhoneIntrospectionParams{
PhoneNumber: zavudev.String("+14155551234"),
})
fmt.Println(result.ValidNumber, result.LineType, result.Carrier.Name)
Ruby:
result = client.introspect.phone(phone_number: "+14155551234")
puts result.valid_number, result.line_type, result.carrier&.name
PHP:
$result = $client->introspect->phone(['phoneNumber' => '+14155551234']);
echo $result->validNumber, $result->lineType, $result->carrier?->name;
电子邮件验证
发送前验证电子邮件地址,以降低退信率。此功能会检测无效语法、无法使用的域名(没有 MX/A 记录)、一次性收件箱、基于角色的地址(info@、contacto@、sales@),以及已列入项目抑制列表的地址。不会探测 SMTP 邮箱:deliverable 表示未发现负面信号,并不保证邮件一定送达。
SDK 中尚未生成相关功能 — 请直接使用 REST 端点:
# Single address
curl -X POST https://api.zavu.dev/v1/introspect/email \
-H "Authorization: Bearer $ZAVU_API_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]"}'
# Batch (max 100 per request)
curl -X POST https://api.zavu.dev/v1/introspect/email \
-H "Authorization: Bearer $ZAVU_API_KEY" \
-H "Content-Type: application/json" \
-d '{"emails": ["[email protected]", "[email protected]", "not-an-email"]}'
响应:
{
"results": [
{
"email": "[email protected]",
"normalized": "[email protected]",
"domain": "example.com",
"verdict": "deliverable",
"reasons": []
},
{
"email": "[email protected]",
"normalized": "[email protected]",
"domain": "deaddomain.example",
"verdict": "undeliverable",
"reasons": ["domain_not_found", "role_address"]
},
{
"email": "not-an-email",
"normalized": null,
"domain": null,
"verdict": "undeliverable",
"reasons": ["invalid_syntax"]
}
],
"summary": { "total": 3, "deliverable": 1, "risky": 0, "undeliverable": 2 }
}
判定结果:
deliverable— 未发现负面信号。risky— 可以发送,但某个信号表明退信或投诉概率较高:role_address、disposable_domain、domain_no_mx(域名可解析,但没有 MX 记录)或suppressed_soft_bounce。undeliverable— 应丢弃这些地址:invalid_syntax、domain_not_found,或因硬退信/投诉而被抑制的地址(suppressed_hard_bounce、suppressed_complaint、suppressed_manual、suppressed_unsubscribe)。
群发前的典型流程:验证列表,丢弃 undeliverable,检查 risky,然后仅添加无问题的收件人。
限制
- 每个联系人最多 20 个渠道
- 渠道标签:最多 50 个字符
- 显示名称:最多 200 个字符
- 不能移除联系人的最后一个渠道
- 不能将联系人与自身合并
- 电话号码必须采用 E.164 格式
- 拒绝跨联系人的重复标识符(请改用合并)