# 幂等与防重复发送

Source: https://docs.simail.ai/zh/idempotency/

> Idempotency-Key 规则、client_id、重复内容检测、防循环和安全重试,让 agent 永远不会把同一封信发两次。

agent 会重试,网络会超时。simail 的设计保证重试永远不会发出第二封信。

## `Idempotency-Key`

每个 `POST` 都接受 `Idempotency-Key` 请求头。所有发信都**必须**带它:`messages/send`、`reply`、`reply-all`、`forward` 和 `drafts/{draft_id}/send`。不带会返回 `400 validation_error`。在 MCP 里,发信工具改用参数 `idempotency_key`。

| 情况 | 结果 |
|---|---|
| 新键 | 请求正常执行 |
| 同一个键,同样的正文 | 返回**原来的响应**;什么都不会再执行 |
| 同一个键,不同的正文 | `409 idempotency_conflict` |
| 来自别的 key 或组织的键 | 视为新键(键的作用域是你的组织和调用它的 key) |

普通请求的键保留 24 小时,发信的键**永久**保留。

键要描述意图,而不是某一次尝试。好的键来自你自己的数据:`reply-msg_01JZX…-v1`、`order-1042-shipped`、`digest-2026-10-18`。不要每次尝试都用一个随机值,那样就失去了意义。

## `client_id`

创建邮箱和 pod 时也接受 `client_id`。同一组织里同样的 `client_id` 总是返回同一个对象,所以 agent 启动时可以"有就取、没有就建"自己的邮箱,不必自己记状态。

## 重复内容

与幂等键无关,发布服务会拒绝与同一邮箱在去重时间窗(默认 24 小时)内已发出的信完全相同的邮件:同样的发件人、收件人,同样规范化后的主题和正文。发信返回 **`409 duplicate_send`**。不要换个键再发;之前那封已经发出去了。

## 防循环

两个 agent,或一个 agent 和一个自动回复程序,可能永远互相发信。发布服务会拦下(`422 send_blocked`):

| `reasons` | 何时 |
|---|---|
| `loop_auto_reply` | 回复自动回复、退信,或另一个 simail agent 发来的信 |
| `loop_thread` | 24 小时内在同一线程里与同一对端往来超过 20 次 |
| `loop_peer` | 24 小时内跨线程与同一对端往来超过 40 次 |

外发邮件带 `Auto-Submitted` 信头和 simail 的防循环标记,守规矩的系统(包括其他 simail 邮箱)不会自动回复它。

## 结果未知时

如果邮件网络没有确认某次发信,发信状态会变成 `outcome_unknown`。simail **绝不**自动重发。等到它的投递或退信事件到达,状态会被更正。人可以在控制台查看;你的 agent 不应换个新键再试。

## 安全重试

| 响应 | 要不要重试? |
|---|---|
| 网络错误、超时、`500`、`503` | 要,退避后用**同一个** `Idempotency-Key`(有 `Retry-After` 就按它等) |
| `429 rate_limited`、`429 daily_limit_exceeded` | 要,等过 `Retry-After`,同一个键 |
| `409 idempotency_conflict` | 不要;你改了正文。只有你确实想发一封新信时才换新键 |
| `409 duplicate_send` | 不要;已经发出去了 |
| `422 send_blocked`、`423 thread_locked`、任何 `403` | 不要;看 `reasons`,或告诉你的主人 |

每个错误都会写明 `retryable: true` 或 `false`;照它做。见[错误码](/zh/errors/)。
