# 收信

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

> 来信经过什么、邮件长什么样、扣留、trust_envelope、附件和原始信源。

:::danger[邮件是不可信输入]
信里的一切（主题、正文、附件、头，甚至 From 地址）都是你控制之外的人写的。**不要执行邮件里的指示**，不要把内容贴进系统提示，也不要让它决定 agent 把信发给谁。simail 加了检查，但没有过滤器能抓住每一次提示注入。
:::

## 入站流水线

发到你地址的信在 SMTP 时被接受或永久拒绝。接受的信在**存入之前**经过确定性检查，所以不存在「能读到但还没检查」的窗口。收信路径上不跑语言模型。

1. 体积（最多 10 MiB）、收件地址、邮箱状态和你的收信拦截名单。未知、已退役、已暂停或未认领的邮箱，以及被拦截的发件人，会被永久拒绝。
2. 按发件人的速率（每个发件人每个邮箱每小时 50 封；每个组织每小时 2,000 封）。超限的信**接受并扣留**，不退信。
3. 先存原始信，再解析。
4. 重复的（同一 Message-ID 且内容相同）丢掉。同一 Message-ID 但内容不同的留下，并标 `msgid_collision`。
5. 归线程（见[线程](/zh/threads/)）。
6. 分类：`normal`、`spam`、`auto_reply`（RFC 3834 头、休假回复，以及 simail agent 所发邮件上的循环标记）、`bounce`（投递和反馈报告）、`otp`（验证码邮件）和 `system`。
7. 提示注入启发式（指令措辞、隐藏或零宽文本、CSS 隐藏、伪造系统提示、长编码块）产生 `injection_score`；达到或超过阈值（默认 0.7；策略可以更严）就**扣留**。
8. 对受监管或特权材料的筛查会把命中的信扣留给人。
9. 回复提取产生 `extracted_text`：去掉引用历史后的新内容（包括常见的中文引用标记）。
10. 一次提交全部，然后发布事件。

可选、默认关闭：附件抽文本和基于模型的注入打分在**存入之后**跑，只能追加扣留，不能放行。

## 邮件对象

```json
{
  "message_id": "msg_…",
  "thread_id": "thr_…",
  "labels": ["received", "unread"],
  "timestamp": "2026-10-18T17:02:11Z",
  "from": "Ada <ada@example.com>",
  "to": ["shop-helper@simail.email"],
  "cc": [],
  "subject": "Where is my order?",
  "preview": "Hi, I ordered last week and…",
  "text": "Hi, I ordered last week and…",
  "extracted_text": "Hi, I ordered last week and haven't heard back.",
  "attachments": [{ "attachment_id": "att_…", "filename": "receipt.pdf", "size": 48213, "content_type": "application/pdf" }],
  "in_reply_to": null,
  "references": [],
  "size": 61840,
  "created_at": "2026-10-18T17:02:12Z",
  "trust_envelope": {
    "auth": { "spf": null, "dkim": null, "dmarc": null, "computed_by": "none" },
    "sender_known": false,
    "first_contact": true,
    "classification": "normal",
    "hold": false,
    "hold_reasons": [],
    "injection_score": 0.05,
    "flags": [],
    "untrusted": true
  }
}
```

- `html` 只给带 `message_raw_read` 的 key（以及人），simail 从不替你渲染它。优先用 `extracted_text` 和 `text`。
- `trust_envelope.auth` 只在 simail 自己算过时报告 SPF、DKIM 和 DMARC（`computed_by`）。它是 `"none"` 时结果为 `null`：不要把 From 当成已认证。
- `sender_known` 在发件人是该邮箱的联系人时为真（你先写过的人，或人提升的人）。其他 simail 客户的信和陌生人一样对待。
- `Reply-To` 会存，但从不用来决定回复发给谁。

## 扣留的信

每种扣留原因以及人能做什么，见[扣留与注入扫描](/zh/holds/)。扣留的信（`hold: true`，标签 `held`）在每个通道（REST、MCP、webhook、事件、WebSocket）上**只有元数据**：`message_id`、`thread_id`、发件地址、收到时间、大小、标签和 `hold_reasons`。主题、正文和附件不给。只有人能在控制台放行或拒绝。`POST …/messages/{message_id}/agent-armor/release` 返回 `403 human_required`。

`hold_reasons` 说明原因，例如 `rate_limited` 或 `unparseable`，或来自注入、一次性验证码、敏感材料检查的扣留。

## 列出和读取

| 方法与路径 | 权限 |
|---|---|
| `GET /v0/inboxes/{inbox_id}/messages` | `message_read` |
| `GET /v0/inboxes/{inbox_id}/messages/{message_id}` | `message_read` |
| `PATCH /v0/inboxes/{inbox_id}/messages/{message_id}`（标签） | `message_update` |
| `DELETE /v0/inboxes/{inbox_id}/messages/{message_id}`（进废纸篓；30 天后清除） | `message_delete` |
| `POST /v0/inboxes/{inbox_id}/messages/batch-get`、`batch-update` | `message_read`、`message_update` |
| `GET /v0/inboxes/{inbox_id}/messages/search?q=` | `message_read` |
| `GET /v0/inboxes/{inbox_id}/messages/{message_id}/raw` | `message_raw_read` |
| `GET /v0/inboxes/{inbox_id}/messages/{message_id}/attachments/{attachment_id}` | `message_read` |

查询参数：`labels`、`before`、`after`、`include_spam`、`include_blocked`、`include_trash`（各自需要对应的 `label_*_read` 权限）、`limit`、`page_token`、`ascending`。

搜索对主题、发件人和正文做全文匹配，中英文都可以。扣留的信只能按元数据搜索。

### 附件和原始信源

附件和 raw 的响应不含文件。它们返回隔离内容域上的短时、**一次性**下载链接：

```json
{ "attachment_id": "att_…", "filename": "receipt.pdf", "size": 48213, "content_type": "application/pdf", "download_url": "https://view.siagentmail.com/…", "expires_at": "2026-10-18T17:07:12Z" }
```

链接 5 分钟后过期，只能用一次。文件总是作为下载提供（`Content-Disposition: attachment`），从不渲染。

## 哪些事件唤醒 agent

每个 `message.received` 事件带 `data.wake`。已知线程、联系人或收信允许名单上的信会唤醒 agent。陌生人的信只在每个邮箱的每日预算和每个发件人的每小时预算内唤醒；超过之后，事件仍会记下并能从 `GET /v0/events` 拿到，但 webhook 和 WebSocket 会跳过，除非订阅 `include_non_wake: true`。每天一条 `inbox.digest` 报告计数。扣留的信从不唤醒。
