← 返回博客

用模板发欢迎邮件(2026):改文案不用重新部署

用 full_access 一次创建 Rovela 欢迎模板(POST /templates),注册流程用 sending_access 通过 template {id, variables} 发送,Idempotency-Key 用 welcome-{userId},并对变量做 HTML 转义——对齐线上文档的 curl 与 Node。

更新于 2026年11月4日
用模板发欢迎邮件(2026):改文案不用重新部署

为什么欢迎邮件应该放进模板

欢迎邮件通常写一次、之后反复改——产品、市场、负责引导文案的人都会动。如果 HTML 写死在注册处理函数里,改个标题都要重新部署。把正文放进 Rovela 模板:创建一次,之后发送只带模板 id 和几个变量即可。

本文对齐线上示例 用模板发欢迎邮件,以及 邮件模板指南 与 Templates API。示例用 curl 创建模板、用 Node fetch 在注册路径发送——与文档同款形态。

这不是欢迎邮件最佳实践(主题、CTA、引导体验),也不是HTML 邮件模板开发(表格布局、内联 CSS、客户端兼容)。相关事务邮件文:带 PDF 附件的发票邮件、Next.js 魔法链接、Express OTP。

密钥:full_access 与 sending_access

模板管理(POST /templates、列表、修改、复制、删除、发布)需要 full_access API 密钥,或控制台 Templates 页。sending_access 打这些接口会得到 403(见 Authentication、Templates API)。

用 template: { id, variables } 走 POST /emails 发信,只需 sending_access。这才是生产注册服务该用的权限:能发欢迎信,不能改文案。

两种密钥都不要放进浏览器包,只在服务端调用。

前置条件

  1. 在 Rovela 控制台有一个已验证的发信域名(Quick Start)。
  2. 一次性创建步骤用 full_access(或控制台会话)。
  3. 应用环境变量 ROVELA_API_KEY 使用 sending_access。
  4. 保存返回的模板 id,例如环境变量 WELCOME_TEMPLATE_ID。

创建一次模板(curl + full_access)

在本机或初始化脚本里跑,不要放进热路径上的注册接口。

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": "Welcome to Acme, {{first_name}}",
    "html": "<h1>Hi {{first_name}},</h1><p>Your workspace <strong>{{workspace}}</strong> is ready.</p><p><a href=\"{{login_url}}\">Open Acme</a></p>",
    "text": "Hi {{first_name}},\n\nYour workspace {{workspace}} is ready.\nOpen Acme: {{login_url}}"
  }'

保存返回的 id(UUID)。生产发送只认这个 id:

{"object": "template", "id": "3b0e7c52-...", "name": "Welcome", ...}

建议同时提供 html 与 text。多数客户端看 HTML;纯文本利于纯文本客户端、部分无障碍配置和反垃圾信号(邮件模板指南)。

占位符精确匹配:{{first_name}} 不会被名为 firstName 的键填充。缺键会原样留下 {{first_name}}——这是有意设计,方便发现漏传,而不是静默空白(Templates API)。

没有条件与循环语法。正文需要逻辑时,在应用里渲染 HTML,直接传 html,不要用 template。

在注册流程里发送(Node + sending_access)

请求里不要带 subject 或 html:二者都来自模板,用 variables 渲染。template 与 html / text 互斥;发送时再写 subject 会被忽略(Templates API)。

变量按原样插入,不会自动做 HTML 转义。 用户输入进 variables 前先转义,否则类似 <img src=x> 的名字会变成邮件里的标记。

const WELCOME_TEMPLATE_ID = process.env.WELCOME_TEMPLATE_ID;

// 文档明确:不会自动转义。用户输入的值先走这里。
function escapeHtml(s) {
  return String(s)
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#39;");
}

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);
  return (await res.json()).id;
}

幂等

注册接口会重试。使用 Idempotency-Key: welcome-{userId}(最长 256 字符;24 小时内重试返回原响应),用户只会收到一封欢迎信。思路与发票附件发送用发票号做键相同。

转义与主题 / 纯文本

同一组 variables 也会填进主题和 text。HTML 转义后的值可能在纯文本里显示成实体(Tom &amp; Jerry 而不是 Tom & Jerry)。名字里可能有 & 或 < 时,给 HTML 与纯文本准备不同变量(welcome-template 示例)。

你自己拼的可信 URL(如上例 login_url)通常不必做实体转义——但不要把未校验的用户输入直接放进 href。

常见问题

  • 收件箱里出现字面量 {{first_name}} —— variables 缺键,或拼写不一致。键名精确匹配。
  • 有人改了线上模板 —— 改动会影响下一次发送,含尚未触发的定时信。更稳妥:POST /templates/:id/duplicate,改副本,先给自己发一封测过,再切换 WELCOME_TEMPLATE_ID(邮件模板指南)。
  • 批量接口 —— POST /emails/batch 直接拒绝 template(不会默默丢掉正文)。欢迎信本来就该每用户一次 POST /emails(Templates API)。
  • 创建模板 403 —— 用了 sending_access。管理操作用 full_access 或控制台。

发布模板(POST /templates/:id/publish)会记录首次上线时间,且幂等——再次发布不会改动该时间戳。

清单

  • 用 full_access(或控制台)创建,模板 id 写入环境变量
  • 生产发信只用 sending_access
  • 注册路径 POST /emails 带 template: { id, variables },不要内联 HTML
  • 用户输入的变量先 HTML 转义;文档不会自动转义
  • 设置 Idempotency-Key: welcome-{userId}
  • 改线上文案:复制 → 修改 → 自测 → 切换 id
  • 不要对模板走 /emails/batch

相关文档