Sign inGet started

从 Resend 迁移

本页将 Resend 的 POST /emails 载荷、屏蔽处理和 Svix 签名的 webhook 映射到 Bird。按顺序遵循主迁移指南,并在步骤 1、3 和 4 中使用这些映射。发送载荷的字段相似,但跟踪、元数据和 webhook 验证需要做出更改。

交给你的 agent

将以下内容粘贴到 Claude Code、Cursor 或 Codex 中。Agent 会根据你的代码仓库逐步执行本页操作,使用它已有的任何 Bird 接口:已连接的 MCP 服务器,或已安装并登录的 CLI。
代码示例
I am moving an email integration from Resend to Bird. Route through it with me.
1. Check what you already have before setting anything up. If Bird's MCP server is connected, use its tools. If the Bird CLI is installed and signed in, use that. Either one is enough, and every step below is an action you take with whichever you have. Only if neither is present, follow https://bird.com/docs/ai/set-up-your-agent.md to set one up and sign me in. Every Bird docs page serves Markdown at its own URL with `.md` appended, so fetch that rather than the HTML.
2. Read https://bird.com/docs/guides/email/migrate/resend.md for the payload, suppression and event mapping, and https://bird.com/docs/guides/email/migrate.md for the order the steps go in.
3. Find and list my Resend usage in this repository before you change anything: the POST /emails and batch call sites and any SDK wrappers around them, the webhook handler and the URL it is registered at, and every domain I send from.
4. Register each of those sending domains with Bird and give me the DNS records to publish, following https://bird.com/docs/guides/email/sending-domains.md. Leave every DNS record my current provider uses exactly as it is: Bird's records are published alongside them and both providers authenticate side by side until I switch traffic. Publishing DNS affects mail for the whole domain, so show me the records and let me publish them.
5. Rebuild my suppression list and import it into Bird before any production traffic goes through Bird, so my first sends do not reach addresses that already bounced or complained. Resend publishes no suppression export, so there is no endpoint that returns this list: derive it from whatever bounce and complaint events I have stored from my webhook, from the dashboard's Emails view, and from contacts marked unsubscribed in any Audience I use. That means the list can be incomplete without either of us noticing, so show me the list you built and tell me which of those sources each address came from before you import anything. The Bird import takes one address per request and is idempotent, so a partial re-run is safe. https://bird.com/docs/guides/email/suppressions.md has the reason taxonomy.
6. Port the send call and the webhook handler using the mapping tables on the provider page. Verification is a header change rather than a rewrite here: Resend signs with Svix and Bird signs per Standard Webhooks, which is the same HMAC construction with the svix-* headers renamed to webhook-*, so keep my verifier and rename what it reads. The event shape does change: Resend's events are scoped to a message and Bird's are scoped to a recipient, so a send to three recipients yields three outcomes rather than one. See https://bird.com/docs/guides/webhooks.md and https://bird.com/docs/guides/email/events.md.
7. Run my whole integration against Bird's mail sandbox before any production traffic, following https://bird.com/docs/guides/email/testing-sandbox.md. Sandbox sends run the real pipeline without reaching an inbox or touching my sending reputation.
8. Stop and ask me wherever a step needs a decision. Do not point production traffic at Bird until I have seen the sandbox results and replied with the words cut over to Bird. Retiring the Resend path is a separate step that comes later: ask me again and wait for me to reply with the words retire the Resend path. A reply that agrees without naming what it is authorising is not authorisation. Finish by telling me what is left that only a person can do.

映射发送调用

功能ResendBird
发件人fromfrom
收件人to / cc / bccto / cc / bcc(数组)
主题subjectsubject
正文html / texthtml / text(至少提供一个)
回复地址reply_toreply_to(数组)
自定义请求头headersheaders(string → string 对象)
可筛选标签tags: {name, value} 键值对tags: {name, value} 键值对
往返上下文(无;标签兼作上下文)metadata: 任意 JSON
定时发送scheduled_atscheduled_at
打开/点击跟踪按域名在面板中设置track_opens / track_clicks(默认 true
类别(无)category: marketing(默认)或 transactional
字段上限和默认值(收件人数量、标签和元数据限制)详见发送邮件
迁移注意事项:
  • 标签保持原有结构,metadata 是一个升级。 Resend 的标签与我们使用的 {name, value} 键值对相同,但其值的限制迫使关联数据被放入标签值中。在这里,请将关联上下文移入 metadata(任意 JSON,会在每个 webhook 事件中回传,也会在 API 读取时返回),标签仅用于筛选。参见标签与元数据的区别
  • 跟踪移入载荷中。 Resend 在面板中按域名切换打开/点击跟踪。我们按消息设置 track_opens/track_clicks(默认均为 true)。
  • scheduled_at 直接映射,字段名也一样。参见定时发送。对于 react,请在应用中将 React Email 模板渲染为 HTML(@react-email/renderrender 函数可直接使用),然后将结果作为 html 发送。
  • 附件直接迁移。 Resend 的 attachments(base64 content)映射到我们的附件数组。为内联图片设置 content_id
  • 批量发送直接迁移。 Resend 的 POST /emails/batch 对应我们的批量端点,两者都返回逐条结果。

导出屏蔽列表

Resend 没有提供专门的屏蔽列表导出功能。请提取最后一个事件为 bouncedcomplained 的地址。使用面板中的 Emails 视图或已存储的 webhook 事件,然后通过导入流程处理该列表。如果你使用 Audiences 发送营销邮件,也请迁移标记为已退订的联系人。

转换 webhook 事件

结果ResendBird
已接受/已处理email.sentemail.acceptedemail.processed
已投递email.deliveredemail.delivered
临时失败email.delivery_delayedemail.deferred
永久退回email.bouncedemail.bounced / email.out_of_band_bounce
垃圾邮件投诉email.complainedemail.complained
已拦截/已屏蔽email.failedemail.rejected
打开email.openedemail.opened
点击email.clickedemail.clicked
退订(无)email.unsubscribed / email.list_unsubscribed
Webhook 验证使用了相关的 HMAC 构造,但请求头不同。Resend 使用 svix-idsvix-timestampsvix-signature。Bird 遵循 Standard Webhooks 规范,使用 webhook-* 请求头。请更新你的验证器,使用 Bird 的签名密钥和 Webhooks 与事件中的流程。
一个行为差异:Resend 的事件以消息为维度。我们的投递事件以收件人为维度(recipient_idemail_id 并列),因此一次发送给三个收件人会产生三个投递结果,每个收件人一个。

正式切换

按照主指南完成域名与 DNS沙箱冒烟测试。两者都与服务商无关。

后续步骤

  • 发送域名:注册、验证生命周期,以及需要重新指向的 DNS 记录
  • Webhooks 与事件:端点设置和 Standard Webhooks 验证
  • 测试沙箱:在正式切换前对新集成进行冒烟测试
  • 屏蔽列表:确认导入的列表以及后续的维护方式