用模板发欢迎邮件
文案放在模板里,发送时只需要一个 id 和几个变量。
欢迎邮件写一次,之后由运营一直改下去。放进模板,这些修改就永远不需要发版——你的注册代码只知道模板 id 和要填进去的值。
1. 创建模板(只做一次)
管理模板需要 full_access 密钥(或者用控制台的 Templates 页面)。在你自己的电脑或初始化脚本里跑,而不是在应用里。
curl -X POST https://api.rovela.dev/templates \
-H "Authorization: Bearer re_your_full_access_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Welcome",
"subject": "欢迎来到 Acme,{{first_name}}",
"html": "<h1>{{first_name}},你好</h1><p>你的工作区 <strong>{{workspace}}</strong> 已经准备好了。</p><p><a href=\"{{login_url}}\">打开 Acme</a></p>",
"text": "{{first_name}},你好\n\n你的工作区 {{workspace}} 已经准备好了。\n打开 Acme:{{login_url}}"
}'记下返回的 id,应用发信时用的就是它。
{"object": "template", "id": "3b0e7c52-...", "name": "Welcome", ...}2. 在注册接口里发送
sending_access 密钥就够了。请求里没有 subject 也没有 html:两者都来自模板,用变量渲染。
const WELCOME_TEMPLATE_ID = process.env.WELCOME_TEMPLATE_ID
// 模板变量是原样插入的,不做 HTML 转义。用户填的内容先过一遍这个函数,
// 否则一个叫 <img src=x> 的名字会变成你邮件里的标记。
function escapeHtml(s) {
return String(s)
.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")
.replace(/"/g, """).replace(/'/g, "'")
}
export async function sendWelcome(user) {
const res = await fetch("https://api.rovela.dev/emails", {
method: "POST",
headers: {
Authorization: "Bearer " + process.env.ROVELA_API_KEY,
"Content-Type": "application/json",
// 每个用户只发一封欢迎信,不管注册流程重试了多少次。
"Idempotency-Key": "welcome-" + user.id,
},
body: JSON.stringify({
from: "Acme <hello@mail.acme.com>",
to: user.email,
template: {
id: WELCOME_TEMPLATE_ID,
variables: {
first_name: escapeHtml(user.firstName),
workspace: escapeHtml(user.workspaceName),
login_url: "https://app.acme.com/login",
},
},
}),
})
if (!res.ok) throw new Error("welcome email failed: " + res.status)
}同一组变量也会填进主题和
text 部分,所以转义过的值在那里会显示成实体——Tom & Jerry 而不是 Tom & Jerry。如果你的用户名里可能有 & 或 <,就给 HTML 和纯文本各用一个变量。可能出的问题
- 收件箱里出现字面量
{{first_name}}。没有匹配键的占位符会原样保留。键是精确匹配的,firstName填不了{{first_name}}。 - 有人直接改了线上模板。修改对下一次发送生效,包括已定时的。先复制一份、改副本、测试,再切换
WELCOME_TEMPLATE_ID。 - 批量发送。
/emails/batch不接受template;欢迎信本来也是每个用户一次请求。
变量、草稿和发布的更多内容见 邮件模板。