Email API 集成(2026):把 Rovela 接到你的应用
Rovela Email API 的集成架构:仅服务端的 sending_access、统一的发送封装、Idempotency-Key 设计、重试,以及经过校验的 webhook 处理。
把 Email API 集成进你的应用
Email API 集成要做的,是让 Rovela 变成一个无聊的依赖:业务 handler 只调用一个模块,密钥从不离开服务器,重试不会重复发送,再加一个 webhook 端点告诉你入队之后发生了什么。这不是「在 Node 里怎么发」(那是在 Node.js 中发送邮件),也不是「注册钩子里怎么发」(那是从应用发送事务邮件)。本文是那些文章底下的架构。
可以从Email API产品页、快速开始、发送邮件 API和身份认证入手。 另见Webhook 概览和Webhook 安全。
本系列相关产品指南:
- 事务邮件 API
- HTTP 邮件 API
- 从 SMTP 迁到 Email API
- SaaS 应用的 Email API
- 用 curl 发送邮件
- 事务邮件 API + 托管邮箱
- 入站与出站 Email API
- 邮件发送 API
- 邮件通知 API
- 批量事务邮件 API
- 事务邮件 Webhook
- 简单的事务邮件 API
- 从应用发送事务邮件
- 已验证域名的 Email API
有哪些部分,各由谁负责
| 部分 | 归属 |
|---|---|
| 已验证发信域名、DNS | 你 + Rovela Dashboard / 域名指南 |
ROVELA_API_KEY(sending_access)、MAIL_FROM | 服务端环境 / 密钥库 |
sendTransactional(…) 封装 | 你的后端 |
每个逻辑动作的 Idempotency-Key | 调用封装的那一方 |
| 队列 / HTTP 重试 | 你的平台 |
| 投递、DKIM、退信 | Rovela |
| Webhook 签名校验 + 事件处理 | 带有 signing secret 的服务端路由(只有在你要读信时才需要 full_access) |
Rovela 不托管你的应用、Worker 或 cron。你调用POST /emails;它负责入队和投递。
1. 先有域名和密钥,再写代码
验证你要用来发信的域名。为应用创建 sending_access Key,只放在运行封装的环境里。也可以把 Key 限制在这一个域名(身份认证,已验证域名的 Email API)。需要注册 webhook 或查看某封邮件时,再用第二个更收紧的进程(或 Dashboard)持有 full_access。轮换用 create-then-revoke。永远不要把 Key 放进 SPA、移动应用或公开仓库。
2. 只保留一个封装
所有产品代码都应走同一个函数,这样请求头和错误处理才能保持一致:
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": `signup-verify:${userId}:${codeId}`,
},
body: JSON.stringify({
from: process.env.MAIL_FROM, // e.g. "Acme <no-reply@yourdomain.com>"
to: user.email,
subject: `Your Acme code: ${code}`,
html: `<p>Your verification code is <b>${code}</b>. It expires in 10 minutes.</p>`,
text: `Your verification code is ${code}. It expires in 10 minutes.`,
tags: { kind: "signup-otp", userId },
}),
});
if (!res.ok) {
const err = await res.json();
throw new Error(`Rovela ${res.status}: ${err.message}`);
}
const { id } = await res.json(); // accepted and queued — not yet delivered
用 Python(urllib / httpx)发同样的头也可以。封装应当:
- 从环境读取
ROVELA_API_KEY和MAIL_FROM。 - 要求调用方传入
idempotencyKey(用户触发的邮件若缺失,就大声失败)。 - 同时发送
html和text(或templateid + 变量)。 - 在非 2xx 时抛错,并带上 API 的
message(文档中的错误形态是{ statusCode, name, message })。 - 把
200当作「已入队」,把 id 写入日志——而不是「已投递」。
键的设计见邮件 API 的幂等键(用 signup-otp:${userId}:${codeId},不要用 Date.now())。
3. 重试与限流
如果 HTTP 客户端或队列会重试,重试必须带上同一把 Idempotency-Key。遇到 429 要退避;若有 Retry-After 请遵守。批量里每封信单独计入限流(发送邮件 API)。不要在超时后换一把新 key 再发一次。
4. 集成里的 webhook 一侧
注册一个 HTTPS 端点(不能是私网或回环地址),只订阅你处理的事件——通常是 email.bounced、email.complained,也许再加上 email.failed。空的 events 数组会订阅全部事件,包括以后新增的类型,这在生产里通常不合适(概览)。
按文档校验 x-resend-signature(t=<unix>,v1=<hex>,对 "<timestamp>.<raw body>" 做 HMAC-SHA256)。按事件 id 去重。一旦你已接收该事件就返回 2xx;只有 5xx、408、429 会重试,最多五次。细节见验证 Webhook 签名、事务邮件 Webhook。
5. 配置与环境
每个环境都使用已验证域名和真实 Key;Rovela 没有另一套行为不同的沙箱模式或测试 Key(快速开始)。开发时发给你能控制的 catch-all,或靠幂等键避免本地反复运行打到真实用户。预发与生产的 Key 和域名要分开,免得压测打到客户。
FAQ
封装要单独做成微服务吗? 只有在很多应用必须共享时才需要。单个产品里,一个模块就够,也更容易把 sending_access 的范围收紧。
模板放在哪? 已存储模板用 full_access 或 Dashboard 管理,由封装按 id 发送(邮件模板 API 指南)。Batch 不能带 template——先渲染,或一封一封发模板邮件。
边缘函数里能放 Key 吗? 可以,前提是它是带机密的服务端运行时,而不是浏览器打包结果。博客里已有 Cloudflare Workers 等专门的发信文章;密钥规则不变。
这次集成不包含什么? 邮箱开通、IMAP 和 webmail(事务邮件 API + 托管邮箱),以及入站解析(入站与出站 Email API)——等产品需要时,再作为独立模块加上。
让发信成为依赖,而不是支线任务
一个封装、一把只用于发送的 Key、强制的幂等键,再加上经过校验的 webhook 处理。这就是一套可以运维的 Email API 集成——之后从应用发送事务邮件里的每个产品事件,都只是一行调用。