Sign inGet started

邮件事件

每个收件人在投递过程中推进时,我们都会发出事件。向三个地址发送一封邮件会产生三条独立的事件流,通过 email_idrecipient_id 关联。本页定义邮件事件类型。签名、重试、排序和重放请参阅 Webhooks
每个收件人从 email.accepted 开始,然后是 email.processed。广播收件人本身就是一条独立消息,因此也有自己的 email.accepted,但仅出现在事件 API 和邮件日志中,而非通过 webhook 投递。此后,消息可能被接收服务器接受(email.delivered)、被延迟并重试(email.deferred,最终变为已投递或已退信)、被接收服务器拒绝(email.bounced),或根本未尝试投递(email.rejected)。投递完成后,事件流还可能产生 email.out_of_band_bounceemail.complainedemail.openedemail.clickedemail.unsubscribedemail.list_unsubscribed
每个收件人最终处于唯一的终态:deliveredbouncedcomplainedrejected,作为 GET /v1/email/messages/{message_id}/recipients 返回的每收件人 status。互动事件不会改变终态:已打开邮件的收件人仍然是 delivered。延迟退信报告会改变终态,因为接收服务器撤回了先前的接受,收件人从 delivered 变为 bounced。消息整体有自己的汇总状态和各状态计数,见 GET /v1/email/messages/{message_id}

事件信封

事件以所有 webhook 通用的三字段信封到达:typetimestamp(事件发生时间,RFC 3339 格式)和一个特定于类型的 data 对象。
代码示例
{
  "type": "email.delivered",
  "timestamp": "2026-07-23T14:51:47.107Z",
  "data": {
    "email_id": "em_01ky7qc398fmxraqtxn604zeq9",
    "recipient_id": "er_01ky7qc398fmwsc96nt6anrbqs",
    "workspace_id": "ws_01ky7m21hjffh9s8kq76gyb1xf",
    "recipient": "delivered@messagebird.dev",
    "recipient_role": "to",
    "tags": [{ "name": "category", "value": "welcome" }],
    "metadata": { "order_id": "ord_123" },
    "broadcast_id": null
  }
}
每个出站事件都包含 email_idrecipient_idworkspace_idrecipient 地址及其信封 recipient_roletoccbcc)。它还会回传发送请求中的 tagsmetadata,以便你将事件与自己的记录关联。每个可选值在发送时未提供的情况下为 nullbroadcast_id 也不例外:它表示该发送所属的广播,便于你将广播的事件分组而无需逐一查找每次发送,在没有广播的发送中它为 null。有一种情况会对实际存在广播的发送报告 null:在我们添加该字段之前发出的邮件中的退订链接不包含广播信息,因此通过此类链接的退出会在 email.unsubscribedemail.list_unsubscribed 上报告 null,无论该邮件是否由广播发出。请将这两个事件上的 null 视为不确定值,否则你会低估广播的退出数量。broadcast_id 仅通过 webhook 到达:下方的事件 API 返回的每个事件不包含此字段。事件类型会添加生命周期、互动、屏蔽和入站部分中描述的字段。
同样的事件也可以事后从 GET /v1/email/messages/{message_id}/events 查询,其中每个事件还额外拥有 idev_ 前缀)和 occurred_at。你可以用它来回填、重放或与你的端点收到的数据进行核对。部分字段仅通过该 API 而非 webhook 到达;相关事件描述会逐一说明。

生命周期事件

email.accepted

我们已接受发送并开始准备投递。每个请求的收件人触发一次,是该事件流中的第一个事件。广播收件人也会收到此事件,因为每个收件人都是独立消息,但它只被记录而不投递:请从事件 API 或邮件日志中读取,而非从你的 webhook 端点。载荷:仅包含身份基础字段。

email.processed

消息已构建并排入队列,准备投递到收件人的邮件服务器。载荷:webhook 上仅包含身份基础字段;事件 API 额外提供 mailbox_providermailbox_provider_region,即接收邮件系统的分类(例如 gmailNA),在能确定时存在,否则为 null。将此事件的 timestampemail.accepted 的时间戳比较,即可得到单次发送的处理时间。广播没有这样的时间间隔:其接受和处理使用同一个调度时刻,因此两个时间戳相同而不构成处理区间,且接受事件仅通过事件 API 获取,显示为 occurred_at

email.delivered

接收邮件服务器已接受消息并承担投递责任。此事件并不代表邮件已进入收件箱或已被阅读。Inbox Insights 提供采样的收件箱到达率估算;打开和点击事件记录跟踪请求。载荷:webhook 上仅包含身份基础字段;事件 API 额外提供 sending_ip(消息的发送地址,当可投递性问题追踪到某个 IP 时尤为重要),以及 mailbox_providermailbox_provider_region

email.deferred

临时失败:接收服务器要求我们稍后重试(邮箱已满、灰名单、请求速率限制)。我们会自动重试,收件人最终变为 email.deliveredemail.bounced,因此此事件是信息性的而非终态,收件人可能先被延迟多次。载荷:webhook 上包含 bounce_typebounce_classdefer_reason(服务器给出的原因)和 sending_ip;事件 API 额外提供 mailbox_providermailbox_provider_region

失败事件

email.bounced

SMTP 时的永久失败:接收服务器拒绝了消息,收件人的终态变为 bounced。载荷:bounce_type(见分类表)、bounce_classbounce_code(SMTP 回复码,例如 550)、bounce_description(服务器给出的原因),以及 webhook 上的 sending_ip;事件 API 额外提供 mailbox_providermailbox_provider_region。硬退信会屏蔽该地址。

email.out_of_band_bounce

延迟退信:接收服务器在 SMTP 时接受了消息,随后发送了退信报告。其分类方式与 email.bounced 相同(webhook 上包含 bounce_typebounce_classbounce_codebounce_descriptionsending_ip;事件 API 提供 mailbox_providermailbox_provider_region)。当报告分类为退信(中的任一类别)时,说明服务器撤回了先前的接受,收件人从 delivered 变为 bounced。类别不在表中的报告(如自动回复)仅记录在时间线上,不改变状态。硬延迟退信同样会屏蔽该地址。

email.rejected

收件人从未到达远程邮件服务器,因此未尝试投递。这就是拒绝与退信的区别:退信是接收服务器拒绝,而拒绝是消息根本没有送出。载荷:rejection_reason,也出现在收件人记录上,值为以下之一:
rejection_reason含义
recipient_suppressed收件人在工作区级别被阻止,由屏蔽列表或已声明的偏好触发,因此未尝试投递
transmission_failed消息无法传输以进行投递
generation_failure消息无法构建以进行投递,属于模板或内容问题
policy_rejection发送策略拒绝了消息
domain_unverified发送域名未验证
quota_exceeded组织的发送配额已用尽
recipient_not_allowed该收件人不被允许用于此次发送;使用共享入门域名发送时只能到达工作区中已验证的成员
当接收邮件系统在拒绝前可以被分类时,事件 API 还会额外提供 mailbox_providermailbox_provider_region

email.complained

收件人将消息标记为垃圾邮件,邮箱提供商通过反馈环路将此信息回报。投诉在投递后到达,并将终态设为 complained。载荷:feedback_type(提供商发送的报告类型,例如 abusefraud),当提供商未说明时为 null,以及来自事件 API 的 mailbox_providermailbox_provider_region。投诉会屏蔽该地址的营销邮件。请保持低投诉率:提供商会限制积累大量投诉的发送者。

互动事件

email.opened

消息正文中的跟踪像素被加载。载荷:已知时包含 ip_addressuser_agent;事件 API 额外提供 is_prefetchedcountry(ISO 3166-1 alpha-2,由客户端 IP 推导)、mailbox_providermailbox_provider_region在计入打开数之前请检查 is_prefetched 当收件箱隐私功能自动获取了像素而非真人打开邮件时,它为 true,计入这些会虚增你的打开率。打开和点击跟踪介绍了检测机制。

email.clicked

收件人点击了一个被跟踪的链接。载荷:url(被点击的链接)、ip_address,以及已知时的 user_agent;事件 API 额外提供 countrymailbox_providermailbox_provider_region。点击通常是比打开更强的互动信号,因为隐私代理可能自动加载跟踪像素。

email.unsubscribed

收件人使用了消息正文中的退订链接。载荷:webhook 上仅包含身份基础字段;事件 API 额外提供 mailbox_providermailbox_provider_region记录退出偏好以阻止营销邮件。退订链接介绍了如何将链接添加到邮件中。

email.list_unsubscribed

收件人使用了邮箱提供商在其自身 UI 中呈现的一键退订按钮,该按钮由消息的 List-Unsubscribe 请求头驱动。载荷:webhook 上仅包含身份基础字段(事件 API 额外提供 mailbox_providermailbox_provider_region);机制即事件类型本身,这也是它与 email.unsubscribed 分开的原因。同样记录退出偏好以阻止营销邮件。

消息级事件

两个事件描述的是整条消息而非单个收件人,因此其 data 包含 email_idworkspace_idtagsmetadata,但没有收件人身份信息。两者均属于定时发送

email.scheduled

我们接受了一个 scheduled_at 设在未来的发送。载荷:消息级基础字段加 scheduled_at。到达该时间后,每收件人的生命周期从 email.accepted 开始。

email.canceled

定时消息在发出前被取消,因此不会产生任何收件人生命周期事件。载荷:仅包含消息级基础字段。

入站和邮箱事件

email.received 涵盖入站邮件。当我们收到并解析入站消息时触发。其载荷包含 inbound_message_id、地址信息、主题和认证判定。设置、载荷和回取 API 请参阅接收邮件邮箱在此基础上拥有自己的 email_mailbox.* 系列事件,详见邮箱指南。

退信分类

bounce_class 是包含在 email.bouncedemail.out_of_band_bounceemail.deferred 上的数值退信分类。它汇总为粗粒度的 bounce_type 并保留细粒度代码,因此即使两者都报告为 soft,你仍能区分邮箱已满和路由故障:
bounce_classbounce_type含义
1undetermined接收服务器的响应含义不明确
10, 30hard永久失败:地址无效或域名不存在
20 to 24, 40, 70, 100soft临时失败:邮箱已满、服务器暂时不可用、DNS 或路由问题
25admin管理性拒绝:中继被拒绝、域名被列入黑名单
50 to 54block接收服务器拒绝了发送 IP
不在此列表中的类别映射为 undetermined。只有 hard 退信会屏蔽该地址;softblockadminundetermined 不会,因为该地址可能仍然可达。

自动屏蔽

两种事件会自动将收件人添加到工作区屏蔽列表,它们阻止的邮件类型不同:
事件屏蔽 reason阻止的内容
email.bouncedemail.out_of_band_bouncebounce_type: "hard"hard_bounce所有邮件,包括事务性邮件
email.complainedcomplaint营销邮件;事务性邮件仍会发送
硬退信阻止所有邮件,因为地址本身已不存在。投诉仅阻止营销邮件,因为举报你新闻邮件为垃圾邮件的人仍然需要收到密码重置邮件。
email.unsubscribedemail.list_unsubscribed 与投诉一样仅阻止营销邮件,但通过不同的记录实现:它们不添加屏蔽条目,而是将收件人的退出记录为已声明的偏好。退出的效果完整介绍了该记录。
每次添加都会触发一个 email_suppression.created 事件,其中包含 suppression_id、被屏蔽的 emailreasonworkspace_id。完整的记录结构以及如何手动管理条目请参阅屏蔽指南
后续向被屏蔽地址的发送会被立即拒绝,原因为 email.rejectedrejection_reason: "recipient_suppressed"),且不会计入你的可投递性指标。

后续步骤