关于 whatsapp-templates
面向使用 Zavu 的开发者,介绍 WhatsApp 模板消息的创建、提交、管理与发送,涵盖模板类别、渠道专属内容、Meta 审批及状态同步。
主要功能
- 01创建含变量和按钮的模板
- 02使用实用、营销和认证类别
- 03设置渠道专属消息正文
- 04提交模板供 Meta 审批
- 05同步 WhatsApp 模板状态与类别
- 06发送带变量值的已批准模板
适用场景
- 01发送订单和配送更新
- 02发送促销或群发消息
- 03发送 OTP 和验证码
- 04同步从 Meta Business Manager 创建的模板
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ zavudev/ zavu-skills --skill whatsapp-templates安装后在 Claude Code 中输入/whatsapp-templates确认已加载。
详细介绍
WhatsApp 模板
使用场景
在编写用于创建、管理或发送 WhatsApp 模板消息的代码时,请使用此技能。要在 24 小时窗口之外发起对话,必须使用模板。
何时需要使用模板
Has user messaged you in the last 24 hours?
-> YES: Send free-form message (text, image, buttons, etc.)
-> NO: Must use template message
Need to send bulk/broadcast messages?
-> YES: Use template (recommended for consistency)
Want proactive outbound notifications?
-> YES: Must use template
模板类别
| 类别 | 使用场景 | 示例 |
|---|---|---|
UTILITY | 交易更新 | 订单确认、发货更新、预约提醒 |
MARKETING | 推广内容 | 促销、优惠、产品公告 |
AUTHENTICATION | OTP/验证码 | 登录验证码、双重身份验证、密码重置 |
创建模板
const template = await zavu.templates.create({
name: "order_confirmation",
language: "en",
body: "Hi {{1}}, your order {{2}} has been confirmed and will ship within 24 hours.",
whatsappCategory: "UTILITY",
variables: ["customer_name", "order_id"],
});
console.log(template.id); // tpl_xxx
Python:
template = zavu.templates.create(
name="order_confirmation",
language="en",
body="Hi {{1}}, your order {{2}} has been confirmed and will ship within 24 hours.",
whatsapp_category="UTILITY",
variables=["customer_name", "order_id"],
)
Go:
template, err := client.Templates.Create(context.TODO(), zavudev.TemplateCreateParams{
Name: zavudev.String("order_confirmation"),
Language: zavudev.String("en"),
Body: zavudev.String("Hi {{1}}, your order {{2}} has been confirmed and will ship within 24 hours."),
WhatsappCategory: zavudev.String("UTILITY"),
Variables: []string{"customer_name", "order_id"},
})
Ruby:
template = client.templates.create(
name: "order_confirmation",
language: "en",
body: "Hi {{1}}, your order {{2}} has been confirmed and will ship within 24 hours.",
whatsapp_category: "UTILITY",
variables: ["customer_name", "order_id"],
)
PHP:
$template = $client->templates->create([
'name' => 'order_confirmation',
'language' => 'en',
'body' => 'Hi {{1}}, your order {{2}} has been confirmed and will ship within 24 hours.',
'whatsappCategory' => 'UTILITY',
'variables' => ['customer_name', 'order_id'],
]);
特定渠道的正文
模板可以为不同渠道设置不同正文。默认的 body 用于 WhatsApp;如果未设置特定渠道的正文,SMS、Telegram 和 Instagram 会回退使用 body。
const template = await zavu.templates.create({
name: "order_confirmation",
language: "en",
body: "Hi {{1}}, your order {{2}} has been confirmed and will ship within 24 hours.",
smsBody: "Order {{2}} confirmed. Ships in 24h.", // SMS fallback
telegramBody: "✅ Order {{2}} confirmed for {{1}}.", // Telegram-specific
instagramBody: "Order {{2}} confirmed!", // Instagram-specific
whatsappCategory: "UTILITY",
variables: ["customer_name", "order_id"],
});
whatsappCategory 仅适用于 WhatsApp 的 body。特定渠道的正文不需要类别。
带按钮的模板
// Quick reply buttons
const template = await zavu.templates.create({
name: "feedback_request",
language: "en",
body: "Hi {{1}}, how was your experience with order {{2}}?",
whatsappCategory: "MARKETING",
variables: ["customer_name", "order_id"],
buttons: [
{ type: "quick_reply", text: "Great!" },
{ type: "quick_reply", text: "Could be better" },
],
});
// URL button
const template = await zavu.templates.create({
name: "track_order",
language: "en",
body: "Hi {{1}}, your order {{2}} has shipped!",
whatsappCategory: "UTILITY",
variables: ["customer_name", "order_id"],
buttons: [
{ type: "url", text: "Track Order", url: "https://example.com/track/{{1}}" },
],
});
// Phone button
const template = await zavu.templates.create({
name: "contact_support",
language: "en",
body: "Need help? Call our support team.",
whatsappCategory: "UTILITY",
buttons: [
{ type: "phone", text: "Call Support", phoneNumber: "+14155551234" },
],
});
// Contact info request button — asks the recipient to share their phone number.
// Label is fixed by WhatsApp ("Share Contact Info"), so `text` is not required.
// Useful for contacts who adopted a WhatsApp username (known only by BSUID).
const template = await zavu.templates.create({
name: "callback_request",
language: "en",
body: "Hi {{1}}, we need a phone number to schedule your callback.",
whatsappCategory: "UTILITY",
variables: ["customer_name"],
buttons: [
{ type: "request_contact_info" },
],
});
OTP 身份验证模板
// Copy code button
const template = await zavu.templates.create({
name: "login_otp",
language: "en",
body: "Your verification code is {{1}}. Do not share this code.",
whatsappCategory: "AUTHENTICATION",
variables: ["otp_code"],
addSecurityRecommendation: true,
codeExpirationMinutes: 5,
buttons: [
{ type: "otp", text: "Copy Code", otpType: "COPY_CODE" },
],
});
// One-tap autofill (Android)
const template = await zavu.templates.create({
name: "login_otp_autofill",
language: "en",
body: "Your verification code is {{1}}.",
whatsappCategory: "AUTHENTICATION",
variables: ["otp_code"],
addSecurityRecommendation: true,
codeExpirationMinutes: 10,
buttons: [
{
type: "otp",
text: "Autofill",
otpType: "ONE_TAP",
packageName: "com.example.app",
signatureHash: "abc123hash",
},
],
});
提交 Meta 审核
模板必须经过 Meta 批准才能使用:
// Submit template for review
const submitted = await zavu.templates.submit({
templateId: "tpl_abc123",
senderId: "snd_abc123",
category: "UTILITY",
});
console.log(submitted.status); // "pending"
通过 Webhook(template.status_changed 事件)或轮询跟踪审核状态:
const template = await zavu.templates.get({ templateId: "tpl_abc123" });
console.log(template.status); // draft | pending | approved | rejected
类别由 Meta 决定,而非由你决定
你提交的类别只是一个申请。Meta 会决定最终生效的类别,并且可以重新分类——最常见的是从 UTILITY 改为 MARKETING——无论是在批准时,还是在模板上线数月后。重新分类会改变该模板每次发送的费用。
template.category 会报告 Meta 当前分配的类别,因此应读取该值,而不要假设你提交的类别仍然有效:
const template = await zavu.templates.get({ templateId: "tpl_abc123" });
console.log(template.category); // UTILITY | MARKETING | AUTHENTICATION
重新分类会通过携带 data.category 的 template.status_changed Webhook 通知。即使批准状态没有变化,该 Webhook 也会触发,因此重新分类事件中的 previousStatus 和 currentStatus 会相同——应根据 category 分支处理,而不只是检查 currentStatus:
if (event.type === "template.status_changed") {
const { templateId, currentStatus, category } = event.data;
if (category !== myStoredCategory(templateId)) {
// Meta moved the billing category. Reprice before the next send.
}
}
若要同步你开始监听之前发生变更的模板,请运行
POST /v1/templates/sync——见下文。
从 WhatsApp 同步模板
在 Zavu 之外创建的模板——例如在 Meta Business Manager 中创建,或由其他工具创建——必须导入后才会出现在 Zavu 中。如果错过了 template.status_changed Webhook,模板会永远保持 pending。如果 Meta 在没有监听器时重新分类了模板,该模板报告的类别也会一直是旧类别。POST /v1/templates/sync 可以解决这三个问题:它会导入 Zavu 中不存在的 Meta 模板(或将它们关联到同名的现有模板),并刷新已有模板的状态和类别。
如果模板的类别已变更、但状态未变更,该模板仍会计入 updated——这是值得运行此操作的情况。
SDK 尚未生成此接口——请通过 REST 调用:
# Sync every WhatsApp sender in the project
curl -X POST https://api.zavu.dev/v1/templates/sync \
-H "Authorization: Bearer $ZAVU_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'
# Or scope it to one sender's WhatsApp Business Account
curl -X POST https://api.zavu.dev/v1/templates/sync \
-H "Authorization: Bearer $ZAVU_API_KEY" \
-H "Content-Type: application/json" \
-d '{"senderId": "snd_abc123"}'
{
"accountsSynced": 1,
"imported": 2,
"linked": 1,
"updated": 3,
"skipped": 12,
"errors": []
}
- 此调用为同步调用:它会等待 Meta 响应,因此每个账户可能需要几秒钟。之后立即列出模板即可查看结果。
skipped统计被跳过的 Meta 模板——已关联,或在 Meta 上被拒绝/禁用(这些模板永远不会被导入)。errors非空但响应为 200,表示一个账户同步失败;其余账户仍已同步。- 如果项目中没有发送方关联 WhatsApp Business Account,则返回 400。
发送模板消息
await zavu.messages.send({
to: "+14155551234",
messageType: "template",
content: {
templateId: "tpl_abc123",
templateVariables: { "1": "John", "2": "ORD-12345" },
},
});
Python:
zavu.messages.send(
to="+14155551234",
message_type="template",
content={
"templateId": "tpl_abc123",
"templateVariables": {"1": "John", "2": "ORD-12345"},
},
)
模板生命周期
draft -> pending (submitted to Meta) -> approved (ready to use)
-> rejected (edit and resubmit)
其他操作
// List templates
const templates = await zavu.templates.list({ limit: 50 });
for (const tpl of templates.items) {
console.log(tpl.id, tpl.name, tpl.status);
}
// Get template
const tpl = await zavu.templates.get({ templateId: "tpl_abc123" });
// Delete template (draft only)
await zavu.templates.delete({ templateId: "tpl_abc123" });
限制
- 模板名称:仅限小写字母和下划线(例如,
order_confirmation),且在每个项目中必须唯一:创建第二个同名模板会返回409 conflict - 变量采用位置格式:
{{1}}、{{2}}、{{3}} - WhatsApp 变量值不得包含换行符、制表符或超过 4 个连续空格。Meta 会在发送时拒绝此类内容,因此 Zavu 会拒绝发送并返回
400 invalid_request,同时在details.field中标明该值。发送前请替换这些字符(例如替换为·);SMS 和 Telegram 模板允许使用这些字符 - 每个模板最多 3 个按钮
- 按钮文本:最多 25 个字符(
request_contact_info不适用——WhatsApp 会固定其标签) - OTP
ONE_TAP需要 AndroidpackageName和signatureHash addSecurityRecommendation仅适用于 AUTHENTICATION 模板codeExpirationMinutes:1-90,仅适用于 AUTHENTICATION- Meta 通常会在几分钟到几小时内完成审核,但最长可能需要 24h
- 被拒绝的模板可以编辑后重新提交