← 返回博客

抑制列表:自动停发退信、投诉和退订地址

Rovela 在硬退信、投诉、退订后自动将地址加入抑制列表,后续发信静默跳过。本文介绍三种自动触发条件、手动管理接口,以及发信前预检的写法。

更新于 2026年10月13日
抑制列表:自动停发退信、投诉和退订地址

发信时遇到退信、投诉或退订,邮件服务提供商通常会要求你停止向那些地址发信。继续发只会损害域名声誉,严重时触发账号封禁。

Rovela 的抑制列表帮你自动处理这些情况:某些事件发生后,地址会自动进入列表;你也可以手动添加。列表里的地址,之后的发信会被跳过,不再投递。

自动抑制的情况

以下三种情况,Rovela 会自动把地址加入列表:

投诉(complained):收件人点了「标记为垃圾邮件」,并且 ISP 将投诉转发给 Rovela。所有投诉都触发自动抑制。

硬退信(bounced,地址无效):服务器明确拒绝,错误码指向地址本身不存在。具体来说是 5.1.1、5.1.0、5.1.3,或者服务器文本里含有 user unknown、no such user、mailbox unavailable、does not exist 等字样。只有这类「地址不存在」的硬退信才会抑制,暂时性退信(服务器满了、暂时不可达等)不会。

退订(unsubscribed):收件人通过 List-Unsubscribe 头发起一键退订,或通过 Rovela 提供的退订页面退订。

手动也可以加:POST /emails/suppressions,指定地址和类型(unsubscribed、bounced 或 complained)。

被抑制时会发生什么

向列表里的地址发信,请求本身会正常接受,返回邮件 ID,但邮件不会实际投递,状态变成 suppressed。如果你配置了 Webhook,会收到 email.suppressed 事件:

{
  "id": "evt_...",
  "type": "email.suppressed",
  "created_at": "2026-10-13T09:00:00Z",
  "data": {
    "email_id": "email_...",
    "to": "user@example.com",
    "reason": "unsubscribed"
  }
}

reason 是 unsubscribed、bounced 或 complained,对应三种自动抑制的来源。

查询和管理

列出列表:

curl https://api.rovela.dev/emails/suppressions \
  -H "Authorization: Bearer $ROVELA_API_KEY"

返回分页结果,每条包含地址、类型(kind)、来源(origin)和时间。origin 有四个值:manual(手动添加)、bounce(自动退信)、complaint(自动投诉)、provider(来自上游提供商)。

手动添加:

curl -X POST https://api.rovela.dev/emails/suppressions \
  -H "Authorization: Bearer $ROVELA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"email": "user@example.com", "kind": "unsubscribed"}'

移除(针对误判或测试地址):

curl -X DELETE https://api.rovela.dev/emails/suppressions/$ID \
  -H "Authorization: Bearer $ROVELA_API_KEY"

删除之后,向这个地址的下一次发信就不再被拦截。用于处理误判时要确认确实是误判,不要轻易对退信地址解禁,因为重新发给不存在的地址还是会退信。

和发信流程的结合

在发信前先查抑制列表,可以在消耗 API 配额前就过滤掉无效地址。适合处理大批量发信:

async function sendIfNotSuppressed(payload) {
  const resp = await fetch(
    `https://api.rovela.dev/emails/suppressions?email=${encodeURIComponent(payload.to)}`,
    { headers: { Authorization: `Bearer ${process.env.ROVELA_API_KEY}` } }
  )
  const { data } = await resp.json()
  if (data.length > 0) {
    console.log(`跳过 ${payload.to}:${data[0].kind}`)
    return null
  }
  return fetch('https://api.rovela.dev/emails', {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${process.env.ROVELA_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(payload),
  }).then((r) => r.json())
}

当然,直接发也可以——Rovela 在投递前还是会检查列表,只是在你自己的系统里提前知道跳过了哪些地址,对记录日志或向用户展示状态会更方便。

更多接口细节见 发送邮件 API 文档。