Sign inGet Started

从 SparkPost 迁移

使用本指南将出站邮件从 SparkPost 迁移到 Bird。按照主迁移清单,使用以下映射完成您的 HTTP 或 SMTP 集成。

开始之前

您需要访问 SparkPost 账户和子账户、发送域 DNS、应用配置和 Webhook 处理程序。在您选择的区域中准备好 Bird 工作区和 API 密钥。退订导入还需要密钥上的 preferences 写入权限。

盘点发件人、模板、代码片段、收件人列表、屏蔽列表、定时发送、IP 池和 Webhook。包括 SDK、框架邮件适配器、后台任务和入站邮件流。创建新资源时记录其 ID;SparkPost 的 ID 和凭据在 Bird 中不可用。替换 SparkPost 客户端时使用 Bird SDK,并检查其重试、超时和分页设置。

如果您使用 SparkPost 子账户,请在选择工作区布局之前联系我们。确认可用的工作区、权限、共享资源和租户配置工作流。工作区 API 密钥无法通过 X-MSYS-SUBACCOUNT 切换租户。在迁移期间保留已暂停的租户和租户特定的发送限制。

确认您的 Bird 套餐配额和请求速率限制能够覆盖您的发送量、资源数量和峰值流量。

尽早注册您的发送域。保留有效的 SparkPost DNS,并在需要时选择单独的 return-path 和跟踪主机名。如果注册提示所有权冲突,请在移除活跃域之前联系支持。

专用 IP:联系我们或您的客户团队,在迁移之前进行沟通。请我们确认您现有的 SparkPost IP 是否可以迁移到 Bird,并就 IP 池设置、时间安排和所需的预热达成一致。提供您的账户区域、IP 地址、池名称和发送量。在迁移计划确认之前,保持当前 IP 处于活跃状态。

在切换之前确认 IP 池选择以及收件方的 IP 或主机名白名单。购买 IP 不会更改默认池。新购买的 IP 可以在预热期间通过共享基础设施发送溢出流量,如果收件方仅接受来自特定 IP 的邮件,这一点很重要。

将以下内容交给你的代理

将以下内容粘贴到应用代码仓库中的编码代理中:

代码示例
Help me migrate my SparkPost email integration to Bird.
1. Use an existing Bird MCP connection or signed-in CLI. Otherwise follow https://bird.com/docs/ai/set-up-your-agent.md. Append .md to Bird docs URLs to read Markdown.
2. Read https://bird.com/docs/guides/email/migrate/sparkpost.md and https://bird.com/docs/guides/email/migrate.md. Make a read-only inventory of my SparkPost call sites, SDKs, resource IDs, configured URLs, sending domains, and scheduled jobs before editing. Never print API keys.
3. Propose workspace and region mappings. If subaccounts, dedicated IPs, or domain ownership conflicts need a migration plan, use the available Bird tools to open a human email support ticket and return its ID. For CLI usage, read https://bird.com/docs/cli/reference/support-tickets-create.md. Wait for the agreed plan before moving those resources.
4. Follow https://bird.com/docs/guides/email/sending-domains.md; preserve working DNS and show me proposed records. Ask before DNS changes or paid provisioning.
5. Port sending, templates, and webhooks using this guide. Preserve recipient privacy, personalization, and categories. Export suppressions with scope and type; show me the handling before importing or changing preferences.
6. Test using https://bird.com/docs/guides/email/testing-sandbox.md. Show me results and unresolved differences, including tracking, signatures, and correlation.
7. Ask for explicit approval before production cutover. Keep a rollback path; get separate approval before retiring SparkPost resources or credentials.

映射发送调用

将 POST /api/v1/transmissions 替换为 POST /v1/email/messages,或使用 POST /v1/email/batches 发送独立消息。SparkPost API 概述列出了其区域主机和身份验证方式。Bird 使用 https://us1.platform.bird.com 或 https://eu1.platform.bird.com,与你的 API 密钥所在区域对应,并使用 Authorization: Bearer $BIRD_API_KEY。

一个 SparkPost transmission 可以为其收件人生成多封独立的个性化邮件。Bird 在一次发送的所有收件人之间共享内容和参数。为每个个性化收件人使用单独的消息,可选择将它们分组到一个批次中。仅含 To 的发送仍然单独寻址;添加 Cc/Bcc 副本时请检查可见的邮件头。

映射 SparkPost transmission 字段:

SparkPostBird 迁移
content.from、subject、html、text同名的顶层字段
content.reply_toreply_to 数组
recipients[].address每个个性化收件人一条消息
address.header_to、content.headers.CC重建 to / cc / bcc 分组;参见下方的地址说明
content.headersheaders;在发送指南中检查保留名称
substitution_data内联 parameters 或存储的 template.parameters;解析覆盖值并满足 Bird 的较小参数限制
content.template_id新建 Bird 模板 id 或 slug;先转换并发布内容
Transmission/recipient metadata合并到 metadata,收件人键优先;满足 Bird 的较小元数据限制
收件人 tags、campaign_id选择 { name, value } 标签;不会创建广播
options.transactional显式 category:transactional 或 marketing
options.open_tracking、options.click_trackingtrack_opens、track_clicks;先解决覆盖项
options.start_timescheduled_at;模板内容在接受时固定;参见下方的调度说明
options.ip_poolBird ip_pool_id;迁移专用 IP 前请联系我们
content.attachmentstype → content_type(基础 MIME 类型)、name → filename、data → base64 content;验证依赖 MIME 参数的文件
content.inline_images相同的文件映射,另加 name → content_id;见下文
return_path、tracking_domainBird 域名配置;见下文
content.ab_test_id在应用中选择变体并跟踪结果;无直接对应的发送字段

对于同一封邮件的 To/Cc/Bcc 副本,只需重建一次收件人组。SparkPost 的显示地址可能与实际投递收件人不同;Bird 的 to、cc 和 bcc 各自添加投递收件人。将 SparkPost 的 CC 邮件头复制到每条展开消息的 cc 中可能会发送重复副本。切换前请验证可见邮件头和收件人数量。

检查发送字段限制、定时发送和附件规则。更新内联图片 ID 及匹配的 cid: 引用,以符合 Bird 的规则。在域名上配置回信路径和跟踪域名。

Bird 的 HTTP 发送字段不包含 SparkPost 的 content.email_rfc822、content.amp_html 或 options.inline_css。使用支持的字段重建原始消息,为 AMP 提供 HTML/文本回退,并在提交 HTML 前内联 CSS。SMTP 会解析并重建支持的消息部分;如果你依赖其精确结构,请验证接收到的 MIME。日历 method 或文本 charset 等附件 MIME 参数不会被保留。

对于定时发送的 API 消息,Bird 在接受请求时即固定模板版本、语言和参数。之后对模板的编辑不会更新该消息。如需更改,请在处理开始前取消定时消息,然后提交替代消息。如果需要替代 SparkPost 基于活动的取消功能,请保留一组 Bird 消息 ID。

将 BIRD_API_KEY 设置为你的 Bird 密钥。此沙箱示例无需验证域名,也不会到达真实收件箱。如果使用 EU 密钥,请使用 https://eu1.platform.bird.com:

代码示例
curl --fail-with-body https://us1.platform.bird.com/v1/email/messages \
  -H "Authorization: Bearer $BIRD_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "from": "onboarding@messagebird.dev",
    "to": ["delivered@messagebird.dev"],
    "subject": "Your receipt",
    "text": "Thanks for your order, {{ first_name }}.",
    "parameters": {"first_name": "Alex"},
    "category": "transactional",
    "metadata": {"order_id": "order_123"},
    "tags": [{"name": "mailstream", "value": "receipts"}]
  }'

预期返回 202 Accepted 和一个 em_ 消息 ID。存储该 ID,并通过事件跟踪收件人结果。接受不等于投递:被屏蔽的收件人可能被接受但随后被拒绝。在发送调用中更新响应解析、错误处理和幂等重试。

对于批量发送,读取 data 数组并将每条消息的 ID 与您自己的发送记录关联保存。Bird 在入队前验证整个批次:一条无效消息可能导致整个请求被拒绝。拆分大型传输以符合批量限制,并在响应不明确时遵循 Bird 的重试规则。

为每个独立的请求或批次分片分配一个稳定的幂等键。Bird 的重放窗口与 SparkPost 的不同;在该窗口之后仍需保留您的应用发送记录,以防止在切换或回滚期间产生重复发送。

迁移 SMTP 发送方

使用 Bird 的 SMTP 连接设置,用户名 bird,以及一个启用了邮件发送权限的 API 密钥。检查区域、TLS 和密钥配置。

在移除邮件头之前,先转换 SparkPost 的 X-MSYS-API 选项。在 Bird 的 SMTP 配置中设置类别、标签、追踪和 IP 池的默认值。这些设置按 API 密钥生效;当不同消息需要不同设置时,使用单独配置的密钥或 HTTP。使用 HTTP 传递每条消息的元数据或模板参数。

将所有投递收件人放入 SMTP 信封,可见收件人放入 MIME To/Cc 邮件头,Bcc 收件人仅放入信封。单独清点 X-MSYS-API.archive:SparkPost 归档副本保留了原始收件人的追踪 URL,因此普通 Bcc 并不等效。在切换该流程前验证替代方案。

显式复制您的有效追踪设置:Bird 未配置的 SMTP 密钥默认启用打开和点击追踪,而 SparkPost 的默认值因帐户而异。同时设置类别:Bird SMTP 默认为事务性邮件,内联 HTTP 默认为营销邮件。Newsletter 发送方在两种路径上都需要 marketing。

进行 SMTP 重试时,复用幂等键、信封和完全相同的 MIME 字节。重新生成 Date、Message-ID 或 MIME 边界会改变载荷,可能导致无法安全重试。

转换模板

通过 SparkPost 的 Templates API 导出您实际发送的版本:使用 GET /api/v1/templates?draft=false 列出模板,然后使用 GET /api/v1/templates/{id}?draft=false 获取内容。如有需要,另外保存草稿。在清单中包含与子帐户共享的模板以及引用的代码片段。

SparkPost 的模板语言与 Bird 的 Liquid 语法不同。转换条件判断、循环、默认值和嵌套值。将用于渲染的收件人覆盖值和元数据解析为显式参数。例如,{{ if ... }} 变为 {% if ... %}。仅凭共享的 {{ name }} 语法不能证明兼容性。

在发布前展开代码片段;Bird 的 Liquid 不支持 include 或 render。对于存储的模板,将外部引用(如 {{ user.name }})替换为扁平参数(如 {{ user_name }})。如果您通过 SparkPost 参数插入动态 HTML,请在应用中渲染后提交完整的正文,不要使用内联 parameters;普通 HTML 参数值会被转义。

创建、预览并发布 Bird 模板,然后按照使用模板发送操作。将 SparkPost 中生效的发件人、Reply-To 和自定义邮件头带入发送请求;Bird 模板提供内容。

对于内联 Liquid,包含 parameters,即使是 {};省略它会导致令牌保持不变。验证缺失值、转义和 URL。

用 {{ bird.unsubscribe_url }} 替换退订占位符。Bird 提供营销退订邮件头;从营销发送中移除自定义 List-Unsubscribe 和 List-Unsubscribe-Post 邮件头,以避免 422 拒绝。

Bird 的退订链接会将该地址从整个工作区的营销邮件中退出。这些链接不提供特定列表的退订。如果你的 SparkPost 集成提供独立的订阅管理,请检查此行为。

迁移收件人列表

使用 GET /api/v1/recipient-lists/{id}?show_recipients=true 导出每个已存储的收件人列表,以包含成员和个性化数据。在导入之前创建目标受众并注册联系人属性。检查每次导入结果并核对成员数量。

联系人属性属于该联系人在其所有受众中的共享数据。如果同一地址在多个 SparkPost 列表中拥有不同的替换数据,请在导入前协调这些值,以避免覆盖。Bird 联系人属性为标量类型;当列表特定或结构化的个性化数据无法安全表示时,请在你的应用程序中保留它们。

当已发布的模板可以通过联系人属性填充时,使用广播。受众成员在发送开始时解析,广播的发送和并发配额适用。对于请求特定的参数或固定的收件人快照,使用独立的批量消息。在激活已迁移的列表之前,验证同意和屏蔽处理。

导出屏蔽列表

在生产发送之前导出。从 GET /api/v1/suppression-list?cursor=initial&types=transactional,non_transactional,open_tracking 开始,然后跟随分页直到完成。保存完整记录,包括类型、来源、列表 ID、子账户和时间戳。主账户使用 X-MSYS-SUBACCOUNT: 0,每个子账户 ID 对应其自身的列表。参阅 SparkPost 的 Suppression List API。

在使用主指南的导入循环之前,按类型、来源和范围对记录进行分类。Bird 的 POST /v1/email/suppressions 接受一个 email,并在两个类别上创建手动的、工作区范围的屏蔽:

  • 被阻止接收邮件的地址: 导入应在所有类别中被阻止的地址。保留原始导出以便核对;导入的记录带有 Bird 的手动原因。
  • 全账户营销退订: 使用 POST /v1/preferences,设置 channel: "email",在 handle 中填入地址,以及 status: "revoked" 和 coverage: "non_transactional"。设置 source: "sparkpost-migration" 以便核对。先检查现有的 Bird 偏好设置并保留更严格的限制;每次写入后检查 applied 和返回的偏好设置。
  • 列表级别或仅限事务性的限制: 在应用程序的发送资格逻辑中保留其作用范围。Bird 的邮件偏好设置是渠道级别的,无法表示这些范围。手动屏蔽也可能阻止密码重置邮件。在验证替代方案之前,暂停受影响的流量。
  • 打开跟踪退订: 为独立消息设置 track_opens: false,同时保留所有发送限制。对于 SMTP,使用已禁用打开跟踪的密钥,或使用 HTTP 进行逐条消息控制。

上述偏好设置请求在导入时记录限制。在导出中保留 SparkPost 的原始时间戳,写入前核对后续的同意变更。核对已导入的记录和失败的写入,然后测试两个类别。在两个提供商同时发送期间同步新的退订和屏蔽列表。切换后继续将之前通过 SparkPost 投递的邮件产生的退订应用到 Bird。有关原生退信和投诉处理,请参阅屏蔽列表。

转换 Webhook 事件

SparkPost 在 msys 包装下发送批量 Webhook 事件。Bird 每个请求发送一个事件,包含 type、timestamp 和 data。注册一个 Bird 端点,明确指定事件订阅和签名验证。保持 SparkPost 处理程序对其剩余流量有效。

SparkPost 事件Bird 事件
injectionemail.processed
deliveryemail.delivered
delayemail.deferred
bounceemail.bounced
out_of_bandemail.out_of_band_bounce
spam_complaintemail.complained
发送端失败(见下文)email.rejected
open、initial_openemail.opened
clickemail.clicked
link_unsubscribeemail.unsubscribed
list_unsubscribeemail.list_unsubscribed

直接 API 和 SMTP 发送在处理前会触发 email.accepted。广播在事件 API 和邮件日志中记录接受状态,但不会发送该 webhook。SparkPost 的 policy_rejection、generation_failure 和 generation_rejection 映射到 email.rejected;检查 rejection_reason。统计唯一互动时,需要对打开事件单独去重。

使用 data.email_id 和 data.recipient_id 进行 Bird 关联,并在 metadata 中携带您自己的标识符。用 Bird 的 webhook 去重和排序规则替换 SparkPost 的 batch-ID 处理逻辑。在决定是否屏蔽某个地址之前,先阅读退信详情;退信分类可区分永久性地址失败与临时性或策略性失败。

保留报告历史

在保留窗口到期之前,导出所需的 SparkPost 事件历史和聚合报告。完整跟踪事件分页,保留提供商 ID、账户/子账户范围、时间戳和报告筛选条件。在重叠期间继续收集延迟到达的事件,并将 SparkPost 历史保存在单独的存档中。

为每个发送流保存基线。比较匹配的收件人群体和报告时间窗口,并检查指标定义:提供商接受、收件服务器投递、唯一互动和预取打开是不同的度量。仅凭指标名称相同并不意味着比率可比。

单独迁移入站邮件

如果你使用 SparkPost relay webhooks,请按照接收邮件指南处理该流程。Bird 的 email.received webhook 提供一个 inbound_message_id;通过 API 获取正文、附件或原始 MIME,而不是在 webhook 中接收完整消息。使用 Bird 转发地址测试你的处理程序,然后在更改 MX 记录之前准备好域名接收配置。DNS 变更后验证回复路由,并归档超出 Bird 接收保留期限的所需内容。

验证并切换

  1. 验证每个域名的发送能力。运行沙盒冒烟测试和投诉案例。确认签名事件到达你的处理程序、关联到正确的消息,并处理重复投递。沙盒事件不能证明收件箱投递、渲染或跟踪效果。
  2. 从你的已验证域名向受控的真实收件箱发送邮件。检查个性化内容、To/Cc/Bcc 可见性、附件、身份验证和跟踪。测试退订:营销邮件必须停止,而符合条件的事务性邮件应继续发送。单独测试全类别屏蔽是否同时拒绝两类邮件。将这些检查与模拟的沙盒结果区分开来。
  3. 将待处理的定时发送分配给一个服务商。在另一端重新创建之前,先排空或取消原始发送。维护一份应用记录,记录每个逻辑发送由哪个服务商接受,以便重试或回滚时不会发送重复副本。
  4. 迁移一部分受控流量,并监控投递指标和 webhook 处理情况。对于专用 IP,请遵循与我们团队商定的迁移计划,包括任何预热步骤。在观察到的结果满足你的投递要求后再增加流量。
  5. 如果验证失败,暂停受影响的 Bird 流量,并通过保留的 SparkPost 路径发送新邮件,同时应用当前的退订设置。在重试之前先核对不明确的发送。在队列和延迟事件处理完毕后,再停用旧的凭据、webhook 和 DNS;保持旧的跟踪和退订链接对已投递邮件继续有效。

如果身份验证失败,检查 Bird bearer token 和区域。如果偏好导入返回 403,在继续之前检查密钥的 preferences 写入权限。如果个性化内容渲染不正确,检查 Liquid 转换和参数。如果事务性邮件被意外拒绝,检查导入的手动屏蔽记录。使用邮件日志和事件详情验证每项修正。

后续步骤