Warmbly文档
端点参考

统一收件箱

集中读取、搜索和分类处理所有邮箱的来信,进行回复、添加标签、稍后处理,并管理定时发送。

统一收件箱(unibox)集中展示组织内所有已连接邮箱收到的内容。这些端点为控制台的收件箱列表、邮件会话视图、范围筛选栏、会话标签、稍后提醒和计划发送队列提供支持。每条路由均受统一收件箱功能权限约束,因此调用组织需要有效试用或付费订阅;否则端点返回 403。

列表、邮件会话、概览、会话标签和稍后提醒相关数据以组织为范围,而非按用户隔离。同一组织的两位成员会看到相同的收件箱、未读标记、邮件会话和会话标签,无论标签由谁添加。稍后提醒是唯一例外:它绑定到调用用户。

列表、邮件会话、单封邮件、概览、未读计数和会话标签读取均有 30 秒超时,包含等待数据库连接的时间;超时的读取会被终止,并返回标准 500 错误体。

列出来信

GET /unibox

返回收件箱列表,每个会话折叠为一行,展示最新邮件,支持筛选和游标分页。身份验证:权限范围 READ_UNIBOX · 组织权限 access_unibox。

参数位置类型说明
cursorquerystring上一次响应中的不透明分页游标。
limitqueryinteger每页数量,会限制在服务器允许的最小值与最大值之间。
fromquerystring按发件地址筛选,采用子字符串匹配。
folderquerystring取值为 inbox、sent、drafts、archive、spam、trash 之一。省略时包含除 spam 和 trash 外的所有文件夹,垃圾邮件不会混入合并视图。未知值返回 400。
subjectquerystring按主题筛选,采用子字符串匹配。
unseenquerybooleantrue 仅返回含未读邮件的会话。
awaiting_replyquerybooleantrue 仅返回最新一封邮件由你发送、收件人尚未回复的会话。
snoozedquerystringtrue 仅返回已设置稍后提醒的会话。默认省略时排除这些会话。
sincequerystring日期下限,格式为 YYYY-MM-DD。
untilquerystring日期上限,格式为 YYYY-MM-DD。
email_idquerystring按 UUID 限定为单个邮箱。
email_idsquerystring逗号分隔的邮箱 UUID。会话只要到达其中任意邮箱即匹配。无效 UUID 会被丢弃。
category_idsquerystring逗号分隔的会话标签 UUID。会话只要具有其中任意标签即匹配。
uncategorizedquerybooleantrue 仅返回未添加任何会话标签的会话。
automatedquerybooleantrue 仅返回没有人工撰写内容的会话,包括安全警报、通知、退信和自动回复,由收件箱自动标记判断。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_idquerystring可选的邮箱 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_idquerystring必填。要读取的会话。也接受参数名 id。
email_idquerystring可选的邮箱 UUID,将会话限定到单个邮箱。也接受参数名 email。
cursorquerystring不透明分页游标。
limitqueryinteger每页数量。超出范围返回 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_idquerystring必填。要读取标签的会话。也接受参数名 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_idstring是要设置标签的会话。
category_idsstring[]否期望的完整标签 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_idsstring[]否要更新的邮件 UUID,最多 500 个。
thread_idsstring[]否要更新的会话,最多 500 个。
folderstring否处理该文件夹中的全部邮件,替代 ID 列表。取值为 inbox、sent、drafts、archive、spam、trash 之一。
seenboolean否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_idstring是发件邮箱 UUID。可使用组织中任意有效邮箱,不限于持有该会话的邮箱。
tostring[]是收件地址,至少一个。
ccstring[]否抄送地址。
bccstring[]否密送地址。
subjectstring是邮件主题。
body_htmlstring否HTML 正文。
body_plainstring否纯文本正文。如果此字段为空且设置了 body_html,会根据 HTML 生成纯文本部分,包含链接目标地址。
in_reply_tostring[]否此回复所归属的父邮件 Message-ID。省略时,使用 thread_id 中最新的 Message-ID。
thread_idstring否要回复的会话。不与 forward_message_id 一同使用;参阅转发邮件。
send_modestring否instant(默认)、smart(邮箱的下一个发送空档),或 scheduled(使用 scheduled_at)。
scheduled_atstring否RFC 3339 时间戳。send_mode 为 scheduled 时必填,且必须是未来时间。
forward_message_idstring否要转发的已存储邮件 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 和纯文本部分均按以下内容发送:

  1. 你的附言,如有;
  2. 开启签名同步时,包含发件邮箱签名,即使没有附言,转发也会添加签名;
  3. 一个 ---------- 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_idstring是要设置稍后提醒的会话。
snoozed_untilstring是取消隐藏会话的时间,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_idquerystring必填。要取消稍后提醒的会话。

响应

204 No Content,响应体为空。

列出定时发送任务

GET /unibox/scheduled

返回从组织邮箱排队、但尚未发出的邮件,无论由哪位成员加入队列。仅允许访问特定邮箱的 API 密钥只能看到这些邮箱的发送任务。传入 thread_id 可限定为单个会话,用于在会话内显示排队回复;两种方式的响应结构相同。身份验证:权限范围 READ_UNIBOX · 组织权限 access_unibox。

参数位置类型说明
thread_idquerystring可选。限定为某个会话中排队的计划发送任务。

响应将条目放在 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_idpathstring要取消的计划任务 UUID。

响应

204 No Content,响应体为空。

按 ID 获取邮件

GET /unibox/:id

按 UUID 返回单封邮件,包含完整信封信息和正文。身份验证:权限范围 READ_UNIBOX · 组织权限 access_unibox。

参数位置类型说明
idpathstring邮件 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。

本页目录