从其他服务商迁移
使用本指南将生产邮件从其他服务商迁移过来。映射发送请求、发布 DNS 记录、导入屏蔽列表、转换 Webhook 事件,并在将生产流量导向 Bird 之前测试集成。
迁移清单:
- 映射你的发送调用到 POST /v1/email/messages
- 重新指向发送域名和 DNS
- 导入屏蔽列表
- 将 Webhook 切换到我们的事件词汇
- 在正式切换前在邮件沙箱中验证
步骤 1、3 和 4 取决于你正在离开的服务商。你的服务商指南包含逐字段的载荷映射、屏蔽列表的导出位置以及 Webhook 事件名称对照表。
1. 映射发送调用
我们有一个单次发送端点 POST /v1/email/messages。你构建一个扁平的 JSON 载荷(无 personalizations 包装,无 MIME 组装),包含 from、to/cc/bcc 数组、subject、html 和/或 text、可选的 reply_to 列表,以及用于自定义邮件请求头的 headers。成功发送返回 202 Accepted,附带以 em_ 为前缀的消息 ID。投递结果通过 Webhook 和读取端点异步到达。完整载荷(包含每个字段的上限和默认值)见发送邮件。从你当前载荷的逐字段映射见你的服务商指南。如果你的应用当前通过 SMTP 提交,可能完全不需要迁移调用:我们接受 SMTP 提交到同一管道,这一步只需更换凭证即可。
使用 tags 作为消息列表、分析和仪表盘汇总中的筛选维度。使用 metadata 存储结构化上下文,Bird 会将其保存在消息上、在 API 读取时返回,并在 Webhook 事件中回传。限制详情见 Tags vs metadata。
在迁移代码之前,注意以下差异:
- 定时发送、存储模板和附件均可迁移。 使用 scheduled_at 进行定时发送。使用 template 代替内联内容来引用存储模板。将文件映射到 attachments 数组。
- 被屏蔽的收件人会被显式拒绝。 被屏蔽的地址仍会获得一个 recipient_id,并出现在消息的收件人列表中,状态为 rejected,附带一个 email.rejected 事件(rejection_reason: recipient_suppressed),不会被静默丢弃。即使所有收件人都被屏蔽,请求仍会以 202 被接受,每个收件人都会返回拒绝状态。详见屏蔽列表。
- 为事务性邮件设置 category: "transactional"。 发送默认使用 marketing,类别控制屏蔽策略:marketing 会因投诉和退订而阻止发送,transactional 则会绕过它们继续投递。新闻通讯和营销活动使用默认值即可正确处理。将收据、密码重置和类似的事务性邮件标记为 transactional,以免被退订所拦截。
2. 重新指向域名和 DNS
使用 POST /v1/email/domains 或在 Email > Domains 中注册每个发送域名,然后发布 dns_records 中的记录。DKIM、return-path CNAME 和 DMARC 策略是发送的前置条件。已有的 DMARC 记录(包括父域上的)也满足要求。跟踪 CNAME 仅用于品牌化的打开和点击跟踪。发送域名涵盖了记录、验证生命周期和区域模型。如果你的 DNS 提供商要求拆分 DKIM 值,使用 DNS 记录拆分工具;如果需要策略,使用 DMARC 策略生成器。
大多数服务商要求你首先添加的一条记录在这里被有意省略了:你不需要在域名顶点发布 SPF 记录。SPF 针对 envelope-from 域名进行验证,而 return-path CNAME 已将其指向我们,因此 SPF 无需修改你的顶点即可通过并对齐。如果你的旧服务商曾让你在顶点 SPF 记录中添加 include:,请在过渡期间保留它,切换完成后再移除。它既不会帮助也不会影响通过我们发送的邮件,移除后可释放顶点 SPF 允许的 10 次 DNS 查找中的一次。完整说明见 DKIM、SPF 和 DMARC。
你可以在旧服务商的记录仍然生效时发布我们的记录。DKIM 记录使用我们的选择器。return-path 和跟踪 CNAME 是你选择的新主机名,你现有的 DMARC 记录无需修改即可满足要求。两个服务商可以同时进行身份验证,直到你准备好切换流量。域名状态是按区域的,因此需要在你发送邮件的每个区域注册域名。
3. 导入屏蔽列表
在通过我们发送生产流量之前迁移你的屏蔽列表。否则,你的首批发送会到达那些在旧服务商已经产生退信或投诉的地址,从而损害你正试图保护的信誉。
从当前服务商导出列表(你的服务商指南中有具体的端点),然后使用 POST /v1/email/suppressions 将每个地址添加到这里:
代码示例
while read -r address; do
curl -s -X POST https://us1.platform.bird.com/v1/email/suppressions \
-H "Authorization: Bearer $BIRD_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"email\": \"$address\"}"
done < suppressions.txt关于此导入方式需要了解两点:
- 每次请求只能导入一个地址。 屏蔽列表 API 是单条目 CRUD,因此大量列表需要循环遍历导出的地址。调用是幂等的(新记录返回 201,地址已被手动屏蔽时返回 200 及现有记录),因此重新执行未完成的导入是安全的。
- 导入的地址会获得 reason: manual、applies_to: all,这会阻止所有类别的发送,包括事务性邮件。这比原生投诉记录更严格,后者仅阻止非事务性发送。如果你需要针对特定地址使用按类别区分的行为,请参阅屏蔽列表中的原因分类。
今后你无需自行管理退信:我们会自动屏蔽硬退信和投诉,并触发 email_suppression.created 以便你的系统同步屏蔽列表。退订会被记录为声明的偏好设置,通过 email.unsubscribed 和 email.list_unsubscribed 同步,而非通过屏蔽事件。
4. 切换 Webhook
使用 POST /v1/webhooks 注册一个端点,并为其订阅明确的事件类型列表。我们的事件名称遵循 resource.action:正常路径为 email.accepted → email.processed → email.delivered,其余由 email.deferred、email.bounced、email.complained、email.rejected、email.opened、email.clicked 及退订事件对覆盖。从你当前服务商词汇的事件名称对照见你的服务商指南。每个事件的载荷结构见事件参考。
关联映射可以直接复用。每个事件都包含 email_id、recipient_id 和 workspace_id 标识符,还会回传发送请求中的 tags 和 metadata。这与旧服务商通过载荷回传返回的上下文相同,无需额外查询。在发送时将你的内部 ID 放入 metadata,即可直接从每个事件中读取。
我们按照 Standard Webhooks 规范对投递进行签名,使用三个请求头:webhook-id、webhook-timestamp 和 webhook-signature,通过对 {id}.{timestamp}.{raw body} 做 HMAC-SHA256 计算。如果你已经在其他平台验证过 Standard Webhooks 投递,完全相同的验证代码在这里同样适用。否则,验证方法、重试计划和重放工具见 Webhooks 与事件。投递是至少一次且无序的,因此需要基于 webhook-id 去重,并按载荷中的 timestamp 排序,这与你当前处理程序应有的做法一致。
5. 切换前在沙箱中验证
在迁移生产流量之前,将你的完整集成(迁移后的发送调用、Webhook 处理程序、屏蔽列表同步)在邮件沙箱中运行一遍。沙箱发送使用 messagebird.dev 的魔法地址,并通过真实生产管道处理:相同的 202、相同的事件序列、相同的签名 Webhook 投递,但不会到达任何收件箱或影响你的信誉。
最小化的切换前冒烟测试:
- 发送到 delivered@messagebird.dev,断言你的处理程序处理了 email.accepted → email.processed → email.delivered。
- 发送到 bounce@messagebird.dev,断言你的退信处理在 email.bounced 上触发(模拟退信不会写入你的屏蔽列表,因此该地址可以继续使用)。
- 发送到 suppressed@messagebird.dev,断言你处理了 email.accepted 后跟 email.rejected,之后没有 email.processed 或投递事件。这就是生产环境中每个被屏蔽收件人产生的事件形态;rejection_reason: recipient_suppressed 详情在收件人记录和事件的 API 中。
- 发送一条 category: "marketing" 消息,确认类别在消息读取中按预期显示。
使用 +label 子地址(bounce+cutover-test@messagebird.dev)来关联测试用例。完整地址会出现在你的事件中。冒烟测试通过后,切换流量。将你的应用指向我们,并保留旧服务商的 DNS,直到你的域名在这里显示为 capabilities.sending 已验证。在仪表盘和 Webhook 流中监控正式投递的最初几个小时。
从特定服务商迁移
- SendGrid:personalizations → 扁平载荷,categories/custom_args → tags/metadata,dropped ↔ email.rejected 的等价关系
- Mailgun:o:*/v:*/h:* 参数 → 一级字段,bounces/complaints/unsubscribes 导出
- Amazon SES:SendEmail v2 → 单一端点,configuration sets → 按消息的跟踪标志,SNS → 签名 Webhook
- Resend:几乎相同的载荷结构,Svix 签名 Webhook → Standard Webhooks
- Postmark:逗号分隔的收件人 → 数组,按流的屏蔽列表导出,无签名 Webhook → 签名 Webhook
- Brevo:地址对象 → 纯地址,两个独立的阻止列表需导出,params → template.parameters
- MailerSend:五个屏蔽列表(其中临时列表无需迁移),personalization → 按发送参数
- Mailjet:Messages 数组 → 单一扁平载荷,EventPayload → metadata,阻止列表导出
- Mandrill:message 包装和 body 认证 → 扁平载荷和 bearer 认证,rejection blacklist 导出
后续步骤
- 发送邮件:完整的发送载荷、tags vs metadata、异步 202 模型
- 发送域名:注册、验证生命周期、多区域设置
- DKIM、SPF 和 DMARC:每条记录证明了什么,以及为什么不需要顶点 SPF
- 屏蔽列表:原因、类别和管理 API
- Webhooks 与事件:端点设置、Standard Webhooks 验证、重试和重放
- 事件:每个事件的载荷结构
- 测试沙箱:完整的魔法地址列表和操作指引
- API 参考:发送端点的请求和响应结构