统一收件箱
集中读取、搜索和分类处理所有邮箱的来信,进行回复、添加标签、稍后处理,并管理定时发送。
统一收件箱(unibox)集中展示组织内所有已连接邮箱收到的内容。这些端点为控制台的收件箱列表、邮件会话视图、范围筛选栏、会话标签、稍后提醒和计划发送队列提供支持。每条路由均受统一收件箱功能权限约束,因此调用组织需要有效试用或付费订阅;否则端点返回 403。
列表、邮件会话、概览、会话标签和稍后提醒相关数据以组织为范围,而非按用户隔离。同一组织的两位成员会看到相同的收件箱、未读标记、邮件会话和会话标签,无论标签由谁添加。稍后提醒是唯一例外:它绑定到调用用户。
列表、邮件会话、单封邮件、概览、未读计数和会话标签读取均有 30 秒超时,包含等待数据库连接的时间;超时的读取会被终止,并返回标准 500 错误体。
列出来信
GET /unibox
返回收件箱列表,每个会话折叠为一行,展示最新邮件,支持筛选和游标分页。身份验证:权限范围 READ_UNIBOX · 组织权限 access_unibox。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
cursor | query | string | 上一次响应中的不透明分页游标。 |
limit | query | integer | 每页数量,会限制在服务器允许的最小值与最大值之间。 |
from | query | string | 按发件地址筛选,采用子字符串匹配。 |
folder | query | string | 取值为 inbox、sent、drafts、archive、spam、trash 之一。省略时包含除 spam 和 trash 外的所有文件夹,垃圾邮件不会混入合并视图。未知值返回 400。 |
subject | query | string | 按主题筛选,采用子字符串匹配。 |
unseen | query | boolean | true 仅返回含未读邮件的会话。 |
awaiting_reply | query | boolean | true 仅返回最新一封邮件由你发送、收件人尚未回复的会话。 |
snoozed | query | string | true 仅返回已设置稍后提醒的会话。默认省略时排除这些会话。 |
since | query | string | 日期下限,格式为 YYYY-MM-DD。 |
until | query | string | 日期上限,格式为 YYYY-MM-DD。 |
email_id | query | string | 按 UUID 限定为单个邮箱。 |
email_ids | query | string | 逗号分隔的邮箱 UUID。会话只要到达其中任意邮箱即匹配。无效 UUID 会被丢弃。 |
category_ids | query | string | 逗号分隔的会话标签 UUID。会话只要具有其中任意标签即匹配。 |
uncategorized | query | boolean | true 仅返回未添加任何会话标签的会话。 |
automated | query | boolean | true 仅返回没有人工撰写内容的会话,包括安全警报、通知、退信和自动回复,由收件箱自动标记判断。false 会排除这些会话,控制台的收件箱视图采用此方式。省略则同时包含两类。只有会话中所有非本人发送的邮件均被判定为自动邮件时,会话才算自动会话,因此收到人工回复后便会重新显示。 |
响应采用 data 加 pagination 封装。每行汇总对应的完整会话(message_count、has_unread),并包含会话标签。
响应
{
"data": [
{
"id": "9b6f0e2a-3c4d-4f1a-8b2e-1a2b3c4d5e6f",
"email_id": "2a1b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"thread_id": "thread-af83b21",
"from_addr": ["Jane Doe <[email protected]>"],
"to_addr": ["[email protected]"],
"subject": "Re: Following up on pricing",
"snippet": "Thanks for the details, this looks great...",
"internal_date": "2026-06-11T14:22:09Z",
"seen": false,
"message_count": 4,
"has_unread": true,
"labels": [
{ "id": "c0ffee00-0000-4000-8000-000000000001", "title": "Interested", "color": "#16a34a" }
]
}
],
"pagination": {
"next_cursor": "eyJpZCI6Ii4uLiJ9",
"has_more": true
}
}获取未读数量
GET /unibox/count
返回收件箱文件夹中的未读会话数,可选择限定为一个邮箱。不计入已设置稍后提醒的会话、自动会话(见上方 automated)及其他文件夹中的邮件(已发送、草稿、归档、垃圾邮件、废纸篓),因此与收件箱视图列出的未读数量一致。用于收件箱未读标记。身份验证:权限范围 READ_UNIBOX · 组织权限 access_unibox。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
email_id | query | string | 可选的邮箱 UUID,用于只统计一个邮箱的未读数。 |
响应
{
"count": 37
}获取收件箱概览
GET /unibox/overview
一次调用汇总范围筛选栏和顶部指标区:未读、今天、本周、稍后提醒、等待回复和待发送计划邮件的数量,以及按文件夹、邮箱、邮箱标签和会话标签划分的明细。folders 数组始终按侧边栏顺序列出六个标准文件夹,缺失数量补零;顶部汇总计数排除 spam 和 trash。除 scheduled_pending 外,所有计数均统计会话,而非单封邮件;该字段统计从组织邮箱排队发送的邮件数,并对应 scheduled_pending_max 中的工作区上限。自动会话计入 automated 和 automated_unread,不计入 unread、today、week、inbox 文件夹,以及按邮箱和邮箱标签的计数;total 和会话标签计数仍包含它们。同一组织的并发请求共用一次计算,结果最多使用到计算开始后一秒,generated_at 即计算开始时间。身份验证:权限范围 READ_UNIBOX · 组织权限 access_unibox。
响应
{
"total": 1284,
"unread": 37,
"today": 12,
"week": 88,
"snoozed": 3,
"awaiting_reply": 9,
"automated": 41,
"automated_unread": 6,
"scheduled_pending": 2,
"scheduled_pending_max": 10000,
"folders": [
{ "folder": "inbox", "unread": 31, "total": 812 },
{ "folder": "sent", "unread": 0, "total": 402 },
{ "folder": "drafts", "unread": 0, "total": 4 },
{ "folder": "archive", "unread": 6, "total": 66 },
{ "folder": "spam", "unread": 2, "total": 9 },
{ "folder": "trash", "unread": 0, "total": 3 }
],
"mailboxes": [
{
"id": "2a1b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"email": "[email protected]",
"name": "Sales",
"unread": 21,
"total": 640
}
],
"tags": [
{
"id": "1f2e3d4c-5b6a-7980-a1b2-c3d4e5f60718",
"title": "Outbound",
"color": "#2563eb",
"unread": 14,
"total": 410
}
],
"categories": [
{
"id": "c0ffee00-0000-4000-8000-000000000001",
"title": "Interested",
"color": "#16a34a",
"unread": 5,
"total": 62
}
],
"generated_at": "2026-06-11T14:25:00Z",
"window_today_start": "2026-06-11T00:00:00Z",
"window_week_start": "2026-06-05T00:00:00Z"
}获取邮件会话
GET /unibox/thread
返回单个会话中的所有邮件,以较早邮件在前的方式展示邮件记录,支持游标分页。邮箱筛选可选:不提供 email_id 时,会跨组织内所有邮箱读取该会话,形成自然的统一视图。身份验证:权限范围 READ_UNIBOX · 组织权限 access_unibox。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
thread_id | query | string | 必填。要读取的会话。也接受参数名 id。 |
email_id | query | string | 可选的邮箱 UUID,将会话限定到单个邮箱。也接受参数名 email。 |
cursor | query | string | 不透明分页游标。 |
limit | query | integer | 每页数量。超出范围返回 400。 |
响应采用 data 加 pagination 封装。每项为一封完整邮件,包含信封信息和正文。如果邮件回复的是由工作区另一个邮箱发送的邮件,例如共享回复收件箱中的回复,则 answers_mailbox_id 包含那个发件邮箱的 ID;其他情况下省略此字段。
响应
{
"data": [
{
"id": "9b6f0e2a-3c4d-4f1a-8b2e-1a2b3c4d5e6f",
"email_id": "2a1b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"mailbox": 1,
"thread_id": "thread-af83b21",
"message_id": "<[email protected]>",
"gmail_id": "18f0c2a9b7d4e5f6",
"parent_id": "<[email protected]>",
"uid": 4821,
"mod_seq": 90210,
"flags": ["\\Seen"],
"bcc": [],
"cc": [],
"from_addr": ["Jane Doe <[email protected]>"],
"in_reply_to": ["<[email protected]>"],
"reply_to": [],
"to_addr": ["[email protected]"],
"answers_mailbox_id": "5e11d0c4-0000-0000-0000-000000000003",
"subject": "Re: Following up on pricing",
"size": 18422,
"internal_date": "2026-06-11T14:22:09Z",
"sent_date": "2026-06-11T14:22:00Z",
"snippet": "Thanks for the details, this looks great...",
"seen": true,
"body_plain": "Thanks for the details...",
"body_html": "<p>Thanks for the details...</p>",
"updated_at": "2026-06-11T14:22:10Z",
"created_at": "2026-06-11T14:22:10Z"
}
],
"pagination": {
"next_cursor": null,
"has_more": false
}
}获取会话标签
GET /unibox/thread/labels
返回附加到会话的标签,与联系人标签来自同一工作区列表,通过 /categories 管理。身份验证:权限范围 READ_UNIBOX · 组织权限 access_unibox。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
thread_id | query | string | 必填。要读取标签的会话。也接受参数名 id。 |
响应将标签放在 data 数组中。
响应
{
"data": [
{ "id": "c0ffee00-0000-4000-8000-000000000001", "title": "Interested", "color": "#16a34a" }
]
}设置会话标签
PUT /unibox/thread/labels
替换会话的完整标签集合,对整个工作区生效。请求体中的 category_ids 表示期望的完整集合,因此调用具有幂等性,可直接安全重试。只会附加工作区自己的标签;属于其他组织的 ID 会被丢弃。身份验证:权限范围 WRITE_UNIBOX · 组织权限 access_unibox。
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
thread_id | string | 是 | 要设置标签的会话。 |
category_ids | string[] | 否 | 期望的完整标签 UUID 集合。空数组会清除全部标签。 |
{
"thread_id": "thread-af83b21",
"category_ids": [
"c0ffee00-0000-4000-8000-000000000001",
"c0ffee00-0000-4000-8000-000000000002"
]
}响应
在 data 数组中返回最终标签集合。
{
"data": [
{ "id": "c0ffee00-0000-4000-8000-000000000001", "title": "Interested", "color": "#16a34a" },
{ "id": "c0ffee00-0000-4000-8000-000000000002", "title": "Demo booked", "color": "#7c3aed" }
]
}将邮件标为已读
PATCH /unibox/seen
在整个组织范围内,将邮件标为已读或未读:可显式指定最多 500 个邮件 ID、最多 500 个会话,或一次处理一个完整标准文件夹。email_ids 和 thread_ids 可同时发送;folder 不能与任一 ID 列表组合,否则返回 400。身份验证:权限范围 WRITE_UNIBOX · 组织权限 access_unibox。
将会话标为已读会将其中所有邮件标为已读。将会话标为未读只会将最新收到的邮件标为未读;如果存在不在垃圾邮件和废纸篓中的收件,则优先选择该邮件。如果会话没有收到的邮件,则将最新的已发送副本标为未读;其他情况下,已发送副本保持已读。草稿永远不会标为未读。显式 email_ids 批量操作和 folder 整体处理也不会将已发送或草稿副本标为未读。
变更也会同步到邮箱本身,因此在这里标为已读的邮件,在 Gmail、Outlook 或 IMAP 邮箱中也会停止显示为未读。通过 GET /unibox/:id 打开邮件时,同样会将其标为已读并同步。只同步实际发生状态变化的邮件,且同步采用尽力执行方式:响应后,由持有该邮箱的工作节点处理;如果当时邮箱未分配到工作节点,它会保留自身已读状态,直到再次发生变更。移动会话到文件夹也以相同方式同步。
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
email_ids | string[] | 否 | 要更新的邮件 UUID,最多 500 个。 |
thread_ids | string[] | 否 | 要更新的会话,最多 500 个。 |
folder | string | 否 | 处理该文件夹中的全部邮件,替代 ID 列表。取值为 inbox、sent、drafts、archive、spam、trash 之一。 |
seen | boolean | 否 | true 标为已读,false 标为未读。 |
{
"email_ids": [
"9b6f0e2a-3c4d-4f1a-8b2e-1a2b3c4d5e6f",
"7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d"
],
"seen": true
}响应
原样回显请求。
{
"email_ids": [
"9b6f0e2a-3c4d-4f1a-8b2e-1a2b3c4d5e6f",
"7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d"
],
"seen": true
}从收件箱回复
POST /unibox/reply
从组织中的任意邮箱立即发送或安排发送回复、转发邮件。发送按 send_mode 通过各邮箱调度器路由。要求组织处于有效状态。身份验证:权限范围 WRITE_UNIBOX · 组织权限 access_unibox。
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
email_account_id | string | 是 | 发件邮箱 UUID。可使用组织中任意有效邮箱,不限于持有该会话的邮箱。 |
to | string[] | 是 | 收件地址,至少一个。 |
cc | string[] | 否 | 抄送地址。 |
bcc | string[] | 否 | 密送地址。 |
subject | string | 是 | 邮件主题。 |
body_html | string | 否 | HTML 正文。 |
body_plain | string | 否 | 纯文本正文。如果此字段为空且设置了 body_html,会根据 HTML 生成纯文本部分,包含链接目标地址。 |
in_reply_to | string[] | 否 | 此回复所归属的父邮件 Message-ID。省略时,使用 thread_id 中最新的 Message-ID。 |
thread_id | string | 否 | 要回复的会话。不与 forward_message_id 一同使用;参阅转发邮件。 |
send_mode | string | 否 | instant(默认)、smart(邮箱的下一个发送空档),或 scheduled(使用 scheduled_at)。 |
scheduled_at | string | 否 | RFC 3339 时间戳。send_mode 为 scheduled 时必填,且必须是未来时间。 |
forward_message_id | string | 否 | 要转发的已存储邮件 UUID,即 GET /unibox/:id 返回的 id。参阅转发邮件。 |
{
"email_account_id": "2a1b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"to": ["[email protected]"],
"subject": "Re: Following up on pricing",
"body_html": "<p>Happy to hop on a call this week.</p>",
"in_reply_to": ["<[email protected]>"],
"thread_id": "thread-af83b21",
"send_mode": "instant"
}响应
{
"task_id": "5c6d7e8f-9a0b-4c1d-8e2f-3a4b5c6d7e8f",
"scheduled_at": "2026-06-11T14:30:00Z",
"send_mode": "instant"
}即时发送会先等待发件人的撤销窗口,范围为 5 至 120 秒,默认 30 秒,然后才真正发出,因此 scheduled_at 会设为相应延迟后的时间。窗口内仍可通过 DELETE /unibox/scheduled/:task_id 取消发送。
如果回复地址直接或因所属域名命中工作区的禁止发送名单,将返回 400,包含 code: "recipient_suppressed" 和指明该地址的 message,不会将任何邮件加入队列。转发不检查此名单。
只有 Gmail 提供服务商会话标识,且该标识属于持有会话的邮箱。因此,只有 email_account_id 在该会话中持有邮件时,回复才会归入 thread_id 指定的 Gmail 会话;如果某个邮箱之前曾从外部参与回复,则会继续使用那次回复创建的会话。其他情况下,邮件仍携带相同的 In-Reply-To、References 请求头和会话主题,使收件方仍将其归入同一会话,而发件邮箱中会创建独立会话。排队发送任务仍属于 thread_id,因此会列在该会话的计划发送列表中。
转发邮件
设置 forward_message_id 可转发收件箱中的邮件。Warmbly 会附上已保存的邮件本身,因此 body_html 和 body_plain 仅作为可选附言,可留空。请求被接受时即复制邮件,因此即使原邮件后来被删除,计划转发仍会携带它。HTML 和纯文本部分均按以下内容发送:
- 你的附言,如有;
- 开启签名同步时,包含发件邮箱签名,即使没有附言,转发也会添加签名;
- 一个
---------- Forwarded message ---------区块,包含原始 From、Date、Subject、To 和 Cc 行,然后是原始正文。
Date 行采用发件邮箱的时区:优先使用邮箱自身时区,其次工作区时区,最后回退到 UTC。原始 HTML 保留格式:样式表合并为内联样式,并像在统一收件箱打开邮件一样进行清理,移除脚本、事件处理器和 Warmbly 自身的打开跟踪像素。如果原邮件只有文本部分,则以文本形式转发。转发内容中的链接永远不会改写为点击跟踪链接;只有在邮箱跟踪直接发送邮件时,才会跟踪你的附言。转发自己已跟踪的发件,例如从“已发送”中转发时,会将跟踪链接还原为真实目标,避免新收件人的点击计入原始发送。
对收件人而言,转发会形成新会话:不使用服务商会话标识,也不携带 In-Reply-To 或 References,除非你自行设置 in_reply_to。同时传入的 thread_id 不用于这两项用途。在统一收件箱内部,无论由哪个邮箱发送,转发都属于被转发邮件的会话,即该邮件的 thread_id:排队期间列在该会话的计划发送中;发件邮箱的“已发送”文件夹同步后,GET /unibox/thread 会在该会话中返回已发送副本,且只保存一次。后续向该会话回复时,绝不会选取转发邮件作为父 Message-ID。
禁止发送名单不适用于转发:转发将已收到的邮件传给人工选择的收件人,因此可以发送到名单中禁止接收营销活动邮件的地址或域名。公平使用额度仍会统计其收件人。
有两项限制:
- 原邮件中的附件不会转发;
- 如果未保存邮件完整正文,阅读器会显示“仅保存了此邮件的预览”,此时仅转发该预览。
转发会披露邮件内容,因此除 WRITE_UNIBOX 外,还需要 READ_UNIBOX;控制台会话的 access_unibox 权限涵盖二者。对于仅允许访问特定邮箱的 API 密钥,必须同时获准访问原邮件所属邮箱和发件邮箱,缺少任一权限都返回 403。如果 forward_message_id 指向的邮件在组织中不存在,则返回 404。
{
"email_account_id": "2a1b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"to": ["[email protected]"],
"subject": "Fwd: Following up on pricing",
"body_plain": "Can you take a look at this?",
"forward_message_id": "7f8e9d0c-1b2a-4c3d-9e8f-0a1b2c3d4e5f"
}列出当前稍后处理的会话
GET /unibox/snoozes
返回你当前有效的会话稍后提醒。身份验证:权限范围 READ_UNIBOX · 组织权限 access_unibox。
响应将稍后提醒放在 data 数组中。
响应
{
"data": [
{
"id": "3a4b5c6d-7e8f-4a0b-9c1d-2e3f4a5b6c7d",
"user_id": "1e2f3a4b-5c6d-7e8f-9a0b-1c2d3e4f5a6b",
"thread_id": "thread-af83b21",
"snoozed_until": "2026-06-12T09:00:00Z",
"created_at": "2026-06-11T14:00:00Z",
"updated_at": "2026-06-11T14:00:00Z"
}
]
}将会话设为稍后处理
POST /unibox/snooze
从你的收件箱隐藏某个会话,直到 snoozed_until 所示时间。采用插入或更新语义:对同一会话再次调用会直接更新时间。身份验证:权限范围 WRITE_UNIBOX · 组织权限 access_unibox。
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
thread_id | string | 是 | 要设置稍后提醒的会话。 |
snoozed_until | string | 是 | 取消隐藏会话的时间,RFC 3339 时间戳。 |
{
"thread_id": "thread-af83b21",
"snoozed_until": "2026-06-12T09:00:00Z"
}响应
{
"id": "3a4b5c6d-7e8f-4a0b-9c1d-2e3f4a5b6c7d",
"user_id": "1e2f3a4b-5c6d-7e8f-9a0b-1c2d3e4f5a6b",
"thread_id": "thread-af83b21",
"snoozed_until": "2026-06-12T09:00:00Z",
"created_at": "2026-06-11T14:00:00Z",
"updated_at": "2026-06-11T14:00:00Z"
}取消会话的稍后处理
DELETE /unibox/snooze
立即取消会话的稍后提醒。此操作具有幂等性:删除不存在的提醒也会成功,返回 204。身份验证:权限范围 WRITE_UNIBOX · 组织权限 access_unibox。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
thread_id | query | string | 必填。要取消稍后提醒的会话。 |
响应
204 No Content,响应体为空。
列出定时发送任务
GET /unibox/scheduled
返回从组织邮箱排队、但尚未发出的邮件,无论由哪位成员加入队列。仅允许访问特定邮箱的 API 密钥只能看到这些邮箱的发送任务。传入 thread_id 可限定为单个会话,用于在会话内显示排队回复;两种方式的响应结构相同。身份验证:权限范围 READ_UNIBOX · 组织权限 access_unibox。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
thread_id | query | string | 可选。限定为某个会话中排队的计划发送任务。 |
响应将条目放在 data 数组中。每项为排队邮件的预览。
响应
{
"data": [
{
"task_id": "5c6d7e8f-9a0b-4c1d-8e2f-3a4b5c6d7e8f",
"scheduled_at": "2026-06-12T09:15:00Z",
"created_at": "2026-06-11T14:30:00Z",
"account_id": "2a1b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d",
"account_email": "[email protected]",
"account_name": "Sales",
"to": ["[email protected]"],
"subject": "Re: Following up on pricing",
"snippet": "Happy to hop on a call this week.",
"thread_id": "thread-af83b21"
}
]
}取消定时发送
DELETE /unibox/scheduled/:task_id
在发送执行前,取消组织任意邮箱中的待发送计划任务;对于仅允许访问特定邮箱的 API 密钥,则限于这些邮箱。队列任务会标为已取消,运行时间到达时直接跳过,不执行任何操作。身份验证:权限范围 WRITE_UNIBOX · 组织权限 access_unibox。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
task_id | path | string | 要取消的计划任务 UUID。 |
响应
204 No Content,响应体为空。
按 ID 获取邮件
GET /unibox/:id
按 UUID 返回单封邮件,包含完整信封信息和正文。身份验证:权限范围 READ_UNIBOX · 组织权限 access_unibox。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | string | 邮件 UUID。 |
响应
{
"id": "9b6f0e2a-3c4d-4f1a-8b2e-1a2b3c4d5e6f",
"gmail_id": "18f0c2a9b7d4e5f6",
"uid": 4821,
"parent_id": "<[email protected]>",
"thread_id": "thread-af83b21",
"flags": ["\\Seen"],
"bcc": [],
"cc": [],
"date": "2026-06-11T14:22:00Z",
"from": ["Jane Doe <[email protected]>"],
"in_reply_to": ["<[email protected]>"],
"message_id": "<[email protected]>",
"ReplyTo": [],
"to": ["[email protected]"],
"subject": "Re: Following up on pricing",
"size": 18422,
"internal_date": "2026-06-11T14:22:09Z",
"mod_seq": 90210,
"body_plain": "Thanks for the details...",
"body_html": "<p>Thanks for the details...</p>"
}错误
所有错误响应均采用标准封装。完整列表见错误码。
{
"error": "forbidden",
"message": "Unibox requires an active trial or paid subscription",
"code": "FORBIDDEN",
"request_id": "req_8f3a1c2b9d"
}常见情况:组织无统一收件箱访问权限时返回 403;缺少 thread_id、游标或每页数量无效、未选择组织时返回 400;一次调用将超过 500 封邮件标为已读时返回 400。