指南 · Resend 邮件 API

产品团队应如何安全地接入 Resend 邮件 API?

请在受信任的服务器工作进程中接入 Resend 邮件 API,而不是放在浏览器或移动端代码中。验证确切的发信域名,在可行时创建一个仅限发送、并限定到该域名的 API 密钥,持久化一个已批准的出站任务,并向 `POST /emails` 传入稳定的 `Idempotency-Key`。保存返回的邮件 ID,在解析之前验证 Webhook 签名,以幂等方式处理事件,并抑制不安全的收件人。请把 API 受理、服务商发送、收件服务器投递和进入收件箱作为彼此独立的状态。

在发起服务商请求之前先定义产品操作

从一个范围明确的应用操作开始,例如账户验证、收据、安全提醒或收件人主动请求的通知。在任何 Resend 请求体生成之前,公开的产品端点就应当对调用方、租户、邮件类别、发件人身份、收件人和模板进行授权。不要让持有可复用凭据的浏览器提交任意的 `from`、`to`、HTML 或服务商选项。创建一条持久化的内部出站记录,包含应用事件键、租户、模板版本、已批准的发件人、收件人集合和当前状态。由工作进程把这条记录转换成服务商请求。这一边界让 API 密钥和不可信的邮件内容远离客户端,使防重复机制可以被测试,也让产品在更换服务商时无需重写每一个业务流程。在内部模型中把事务性邮件与依赖许可的邮件分开,使偏好设置、抑制和事故处理决策保持明确。

验证 From 地址中实际使用的域名

在 Resend 中添加一个您控制的域名,并发布为该域名显示的 DNS 记录。请验证可见 From 地址中实际使用的组织域名或子域名,而不是假设某个无关的上级身份已经覆盖了它。在修改 DNS 之前先检查现有的 SPF 和 DMARC 策略,切勿为同一个主机名创建第二条 SPF 记录。当隔离、归属或迁移需求有必要时,使用专用的发信子域名。在控制台报告验证通过之后,检查一封收到的测试邮件,确认可见 From 地址、DKIM 签名身份、Return-Path、身份验证结果和回复行为。服务商验证只能证明所配置的身份通过了服务商的设置检查。它不能证明收件人许可、收件服务器接收、进入收件箱或良好的信誉。请在服务商控制台之外保留 DNS 归属和变更历史,以确保仍然可以轮换和回滚。

为每个工作负载创建最小权限的 API 密钥

Resend 的文档说明 API 密钥可以设置访问级别,并可选择限定域名。发信工作进程应使用仅限发送权限的密钥,并在架构允许时,将其限定到该工作负载所拥有的那个域名。将管理、域名、Webhook 和账户管理权限交由独立的权限主体掌握。为开发、预发布和生产环境分别创建密钥,以免较低级别的环境使用生产身份发信或占用生产环境的额度。将每个密钥直接保存在托管的机密信息存储中,只暴露给需要它的服务器进程,并通过 HTTPS 以 Bearer 授权的方式传递。不要把密钥复制到源代码仓库、构建产物、日志、模板、分析系统、工单或提示词中。轮换流程应事先演练:创建一个作用域相同的替代密钥,更新工作进程,验证受控流量和事件关联,然后撤销旧密钥。对意外的身份验证和授权失败发出告警,因为它们可能意味着密钥过期、被撤销、作用域漂移或机密信息泄露。

一个持久化任务,对应一次幂等的发送尝试

在调用 Resend 之前先预留内部出站任务。根据稳定的产品事实(例如租户、操作类型和不可变的应用事件 ID)派生幂等值,而不是根据某次随机的重试尝试生成。在 `Idempotency-Key` 请求头中发送该值。Resend 目前的文档说明,这些键可以防止重复的邮件请求,24 小时后过期,最多 256 个字符。这个服务商提供的时间窗口很有用,但并不是完整的产品级防重复保证。对于持续时间更长的业务流程,请在内部事件键上保留唯一性约束,对可能认领同一任务的工作进程进行串行化,并保存成功请求返回的服务商邮件 ID。如果网络超时导致无法确定是否已受理,请将任务置于“未知”状态,在重新发送之前根据服务商日志或事件进行核对。对同一个逻辑操作复用一个稳定的键,比每次传输重试都生成新键更安全。

审慎地构建和校验邮件请求

Resend 的发送邮件端点接受 From 地址、收件人、主题和邮件内容,并提供文档中说明的选项,例如纯文本、HTML、React 渲染的内容、模板、Cc、Bcc、reply-to、邮件头、附件、标签和定时发送。只开放产品需要的那部分选项。校验地址语法和租户归属,把收件人和附件数量限制在服务商上限之下,拒绝邮件头中的换行注入,并通过持续维护的库或可信的服务商字段构建与 MIME 相关的内容。不要在标签或邮件头中放入凭据、敏感个人数据或不受限制的客户输入。保存模板版本和经过清理的变量,而不是记录完整内容。内部适配层应返回一个范围明确的结果,例如已受理的服务商 ID 或经过分类的失败,而不是把服务商响应的细节泄露到业务代码中。这样一来,在更新服务商特定的字段名、SDK 版本或请求限制时,无需改变产品事件的接口约定。

重试之前先对 API 响应和用量限制进行分类

请把 HTTP 响应视为工作流中的一次观察结果。成功的发送响应会返回一个邮件标识符,应将其与内部任务一起保存,但它并不能证明目标服务器已接收或邮件已进入收件箱。对于校验、身份验证、域名、权限和请求体错误,请修正问题,而不是盲目重试。Resend 的文档说明了 API 请求限制,并会返回速率限制和配额相关的响应头,包括描述剩余容量、重置时间和重试延迟的字段;遇到 429 响应时,应按文档规定的间隔等待,并加上抖动。对于传输失败和符合条件的服务器错误,在文档规定的时间窗口内,使用指数退避、有限的尝试次数和同一个逻辑幂等键进行重试。结果不明确的失败需要核对,因为即使客户端没有收到响应,服务商也可能已经受理了邮件。当重复失败集中出现在某个域名、模板、密钥或租户上时发出告警,但不要在运维日志中记录凭据、完整内容和不必要的收件人数据。

处理事件之前先验证 Webhook 请求

配置一个专用的 HTTPS Webhook 端点,并保留原始请求体的确切内容。Resend 的文档说明 Webhook 通过与 Svix 兼容的请求头和签名密钥进行签名。在进行 JSON 解析或重新序列化之前,请基于未经修改的负载验证 Webhook ID、时间戳和签名,并使用官方的验证流程或持续维护的兼容库。拒绝无效或过时的请求,限制请求大小,并将签名密钥与发信密钥分开保管。完成身份验证后,先将事件持久化存储或入队,再返回确认,以免进程崩溃时悄无声息地丢失投递证据。投递系统可能会重试和重复推送 Webhook,因此请使用事件标识符作为去重键,并让状态转换保持单调。较晚到达或重复的事件,不能仅仅因为最后到达就覆盖一个信息更完整的终态结果。把验证失败和事件延迟记录为运维信号,并且不要在必要的保留期限之外保存原始邮件内容。

为服务商事件建模,但不夸大投递结果

Resend 发布了一系列具名的邮件事件类型,包括 sent、delivered、delivery delayed、bounced、complained、failed、opened 和 clicked。请把这些服务商名称映射到内部状态模型中,同时保留原始事件类型、服务商邮件 ID、事件 ID、时间戳、收件人范围以及可用的诊断数据。sent 事件描述的是服务商侧的进展。delivered 事件按照 Resend 文档中的事件语义报告投递,但收件系统返回 SMTP 成功,仍然无法说明收件人最终的文件夹。打开和点击是互动方面的观察结果,不是投递证明,而且可能受到隐私保护技术的影响。退信、投诉和永久失败应在下一次发送决策之前更新收件人安全状态。让服务商事件历史保持只追加,并根据明确的规则推导面向用户的状态。这样可以为客服保留证据,并避免在投递责任已经转移或收件人已给出负面信号之后进行不安全的重试。

用受控收件人测试失败和恢复路径

使用非生产环境的密钥、一个受控的已验证子域名,以及团队自己拥有的邮箱。测试纯文本和 HTML 内容、reply-to 行为、附件限制、稳定的幂等键以及保存的服务商标识符。将同一个逻辑任务提交两次,确认应用和服务商的控制机制不会产生意外的重复。演练以下场景:无效请求体、错误的域名、已撤销的密钥、权限不足、速率限制、传输超时、退信、投诉、投递延迟、重复的 Webhook、签名正文被修改、过时的 Webhook 时间戳以及签名密钥轮换。确认事件接收在返回确认之前已完成持久化,并且收件人安全状态会阻止之后的任务。测试 DNS 轮换和移除服务商的过程,同时不删除无关的记录。仪表板应涵盖发送失败、延迟、Webhook 验证失败、事件滞后、退信、投诉和待核对队列。上线时请查阅 Resend 当前的文档和账户设置,因为配额、限制、事件字段和可用权限都可能独立于已部署的应用代码而发生变化。

迁移前比较已发布的 API 功能

SendHQ 为其限定工作区作用域的邮件 API 发布 OpenAPI 3.1 接口约定,其中包括已验证域名发送、入站邮件、托管模板、投递事件和抑制记录。迁移集成前,请比较请求体、身份验证、幂等性、返回标识符、错误结构、Webhook、域名规则和抑制行为,然后通过字段级测试验证。不要因端点名称相似就假设兼容。

常见问题

通过 Resend 发送邮件使用哪个端点?

Resend 的文档给出的是 `POST https://api.resend.com/emails`,使用 Bearer 授权。只能在对产品操作、发信域名、收件人和内容完成授权之后,从受信任的服务器代码中调用它。

Resend API 密钥的作用域应如何设置?

使用具有发送权限的密钥,并在文档所述的控制能力符合您的架构时,将其限定到该工作负载的域名。生产、非生产和管理权限应分别使用由机密信息管理器保管的独立凭据。

Resend API 返回成功,能证明邮件已送达吗?

不能。它记录的是服务商已受理,并返回一个邮件标识符。之后经过身份验证的事件可以报告服务商侧的进展和收件系统的投递情况,而是否进入收件箱仍是收件方另行做出的分类。

Resend 的幂等机制如何防止重复发信?

对同一个逻辑请求发送一个稳定的 `Idempotency-Key`。Resend 目前将键保留 24 小时,最长 256 个字符,因此还应在内部保留一个有效期更长的唯一性约束。

应如何验证 Resend Webhook 签名?

保留原始请求体的确切内容,并在解析之前验证文档中说明的、与 Svix 兼容的 Webhook ID、时间戳和签名请求头。拒绝无效或过时的输入,然后在返回确认之前将经过验证的事件持久化入队。

SendHQ 可以替代 Resend 吗?

在将 SendHQ 和 Resend 视为兼容前,请比较已发布的 API 接口约定并运行字段级集成测试。

参考来源