← 返回博客

批量事务邮件 API(2026):用 Rovela 一次发送最多 100 封个性化消息

使用 Rovela 的 POST /emails/batch 一次发送最多 100 封个性化事务邮件:校验、抑制名单、按分片的 Idempotency-Key,以及何时改走广播。

更新于 2026年11月10日
批量事务邮件 API(2026):用 Rovela 一次发送最多 100 封个性化消息

批量事务邮件 API(不是营销群发)

批量事务邮件 API 用一次 HTTP 请求发出许多个性化消息——试用提醒、续费通知、按用户的摘要——但不会把它们变成通讯。Rovela 的端点是 POST https://api.rovela.dev/emails/batch:最多 100 条普通发送正文,每条有自己的收件人和内容。

这不是批量发信或批量发信与单封发送(更宽的批量 vs 单封),也不是广播。营销名单展开、一键退订和主题抑制,请用广播(事务邮件与营销邮件)。字段级参考见发送邮件 API;下面的行为说明依据批量与定时示例。

可以从Email API产品页、快速开始、发送邮件 API和身份认证入手。

本系列相关产品指南:

请求

curl -X POST https://api.rovela.dev/emails/batch \
  -H "Authorization: Bearer re_your_sending_key" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: reminders-2026-11-10" \
  -d '[
    {
      "from": "Acme <reminders@mail.acme.com>",
      "to": "alice@example.com",
      "subject": "Your trial ends in 3 days",
      "html": "<p>Hi Alice, your trial ends on Nov 13.</p>",
      "text": "Hi Alice, your trial ends on Nov 13."
    },
    {
      "from": "Acme <reminders@mail.acme.com>",
      "to": "bob@example.com",
      "subject": "Your trial ends in 3 days",
      "html": "<p>Hi Bob, your trial ends on Nov 13.</p>",
      "text": "Hi Bob, your trial ends on Nov 13."
    }
  ]'

响应是 { "object": "list", "data": [ { "object": "email", "id": "…" }, … ] },顺序与你提交的数组相同。为这一整块带上 Idempotency-Key(例如 digest-2026-11-10-0),这样任务崩溃后可以安全重试该分片(邮件 API 的幂等键)。

每条使用与 POST /emails 相同的模式(from、to、subject、html / text、scheduled_at、attachments、tags……),但有两个刻意的例外:template 和 topic_id 会被拒绝,而不是被忽略。如果 batch 默默丢掉 topic_id,可能写信给已退订该主题的人;如果丢掉 template,就会发出没有正文的信。这些请走一次一次的 POST /emails,或先自己渲染 HTML 再批量。

一批请求会如何表现

根据批量示例:

  • 在写入或扣费之前先做校验。 一条坏记录——域名未验证、正文为空——会让整单以指出问题的 400 被拒绝。先搞清楚有没有受理任何东西再重发同一把幂等键(400 表示什么都没写入)。
  • 受理之后,各条独立投递。 某一收件人后来退信,不会收回其他信。
  • 被抑制的地址会被跳过,而不是整单拒绝。 与单封发送不同,batch 不会因为某收件人已被抑制而拒收该条。它会被受理,然后在投递时跳过,状态为 suppressed,并产生 email.suppressed 事件。
  • 按收件人计费,并不比 100 次单封更便宜。 100 封的一批与 100 次单封费用相同,限流也按同样方式计算(大型任务中途可能遇到 429;每封单独计数)。把任务拆成多批是为了更快,不是为了更便宜。
  • 每条都可以带 scheduled_at,同样的 ISO 8601 规则(必须带偏移)。抑制名单会在投递时再检查一次(定时发送邮件)。

每条内部的 to+cc+bcc 仍然受 50 人上限约束。

超过 100 封时

async function sendAll(messages) {
  for (let i = 0; i < messages.length; i += 100) {
    const chunk = messages.slice(i, i + 100);
    const res = await fetch("https://api.rovela.dev/emails/batch", {
      method: "POST",
      headers: {
        Authorization: "Bearer " + process.env.ROVELA_API_KEY,
        "Content-Type": "application/json",
        "Idempotency-Key": "digest-2026-11-10-" + i,
      },
      body: JSON.stringify(chunk),
    });
    if (res.status === 429) {
      await new Promise((r) => setTimeout(r, 1000 * Number(res.headers.get("retry-after") ?? 1)));
      i -= 100; // 同一分片再来一次
      continue;
    }
    if (!res.ok) throw new Error("chunk " + i + ": " + (await res.json()).message);
  }
}

这段模式来自官方示例。Worker 上使用 sending_access。

什么时候不要用 batch

FAQ

一批里能混用 scheduled_at 和立即发送吗? 每条都是独立消息;只给需要等待的条目设置 scheduled_at。示例写明:单封或 batch 条目都可以带 scheduled_at。

如何取消一批里的某一封? 用 full_access 调用 POST /emails/:id/cancel,且该信仍处于 scheduled,或已入队但尚未交给邮件服务器——与单封相同(用 curl 发送邮件)。

API 参考里的「独立处理」和「全部校验或全部失败」矛盾吗? 把它们放在一起读:示例明确写了校验在写入/扣费之前是整单进行的;一批被受理之后,投递结果按每封计算。拿不准时,把 400 当作「没有入队」,并检查 200 返回的 id 列表。

批量个性化,但保持事务性

在任务里渲染每封正文,按 100 封一组 POST /emails/batch,并为每个分片设置幂等键。这就是批量事务邮件 API——适合提醒和摘要,也故意不适合做活动群发。