邮箱
连接、配置、预热和验证发件邮箱(邮箱账户),并通过这些邮箱发送邮件。
邮箱是 Warmbly 用于发送营销活动和预热邮件的发件账户。这些端点位于 /emails 下,可列出和查看已连接邮箱、更新发送和预热设置、配置邮箱自定义跟踪域名、管理预热生命周期、检查域名身份验证和封禁状态、在发送前验证地址,以及从特定邮箱发送一次性邮件。
多数读取路由需要读取邮箱权限范围,写入路由需要写入邮箱权限范围。邮箱连接和初始接入路由仅接受会话,因为它们通过 SPA 弹窗流程写入由用户密钥加密的刷新令牌。发送路由需要发送营销活动权限范围,因为会实际发送邮件。API 密钥仅限特定邮箱时,每条 /:id 路由还会检查密钥是否获准操作该邮箱。
列出邮箱
GET /emails
返回组织已连接的邮箱,按最新优先排序,支持游标分页。
身份验证:权限范围 READ_EMAILS · 组织权限 view_campaigns
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
q | query | string | 可选。对邮箱地址和名称进行自由文本搜索。 |
tag | query | string (UUID) | 可选的标签 ID 筛选,必须为有效 UUID。 |
cursor | query | string (UUID) | 此前 pagination.next_cursor 中的不透明游标。 |
limit | query | integer | 每页数量,默认 50。无效值返回 400。 |
响应
邮箱对象的 data 数组,加上 pagination 封装。
{
"data": [
{
"id": "0c0f1a2b-3c4d-5e6f-7a8b-9c0d1e2f3a4b",
"user_id": "a1b2c3d4-...",
"organization_id": "f9e8d7c6-...",
"worker_id": "7b6a5c4d-...",
"email": "[email protected]",
"name": "Acme Sales",
"signature_plain": "",
"signature_html": "",
"signature_sync": false,
"signature_code": false,
"send_as_email": "",
"provider": "gmail",
"mail_host": "google_workspace",
"auth_method": "oauth",
"avatar_url": "",
"status": "active",
"last_synced_at": "2026-06-11T09:14:00Z",
"last_id": 184213,
"campaign_limit": 50,
"min_wait_time": 600,
"reply_to": "",
"tracking_domain": "t.acme.com",
"tracking_domain_verified": true,
"tracking_domain_verified_at": "2026-06-01T12:00:00Z",
"auth_state": "passing",
"auth_spf": true,
"auth_dkim": true,
"auth_dmarc": true,
"auth_dmarc_policy": "quarantine",
"auth_reason": "SPF, DKIM and DMARC all present (DMARC policy: quarantine)",
"auth_checked_at": "2026-06-11T03:00:00Z",
"auth_failing_since": null,
"warmup": "2026-05-20T00:00:00Z",
"warmup_paused_at": null,
"warmup_base": 10,
"warmup_max": 40,
"warmup_increase": 1,
"warmup_reply_rate": 30,
"warmup_tag": "",
"warmup_pool_type": "premium",
"warmup_start_time": "09:00",
"warmup_end_time": "17:00",
"warmup_days": 31,
"timezone": "America/New_York",
"tags": ["outbound"],
"created_at": "2026-05-19T18:00:00Z",
"updated_at": "2026-06-11T09:14:00Z"
}
],
"pagination": {
"total": 12,
"next_cursor": "c1_b3BhcXVlLWN1cnNvcg",
"has_more": true
}
}provider 为 gmail、outlook 或 smtp_imap,表示 Warmbly 与邮箱通信的方式。mail_host 表示实际邮件托管服务商,与传输方式无关,取值为 google_workspace、gmail、microsoft365、outlook、zoho、yahoo、aol、icloud、fastmail、godaddy、namecheap、ionos、hostinger、ovh、migadu、purelymail、rackspace、yandex、gmx、proton 或 other。auth_method 表示登录方式:password、app_password、oauth 或 delegated。通过应用密码连接的 Google Workspace 邮箱,其 provider 为 smtp_imap,mail_host 为 google_workspace,auth_method 为 app_password。相关信息尚未知时,字段可能为空字符串;尚未记录托管服务商的邮箱会在后台根据域名 DNS 分类。avatar_url 是邮箱自身头像,在 Microsoft 邮箱连接或重新连接时读取,也可通过管理员授权或导入邮箱的供应商获取,后两种来源每周重新检查。无法读取时为空字符串,包括单独登录的 Google 邮箱及所有 SMTP 邮箱。status 为 active、inactive 或 revoked。warmup 是预热起算时间戳,从未启用预热时为 null;warmup_paused_at 非 null 表示预热已启用但暂停。auth_state 为发件域名身份验证状态,取值为 passing(存在 SPF 和 DMARC)、failing(缺少 SPF 或 DMARC)或 unknown(尚未检查、DNS 查询未能完成,或域名为无法解析的特殊用途域名),由后台检查刷新。auth_dkim 仅能提供肯定证据,且永不作为发送准入条件:true 表示在探测的选择器上找到了 DKIM 密钥,false 表示均未响应,这属于尚未验证,而非缺少记录,因为无法通过 DNS 枚举选择器。auth_failing_since 是域名进入 failing 的时间,其他情况为 null;如果域名保持 failing 超过实例宽限期,该域名下所有邮箱的冷邮件发送和预热都会停止,因此此时间决定何时生效。参阅域名身份验证。total 和 next_cursor 在不适用时可能为 null。
获取邮箱
GET /emails/:id
按 ID 返回单个邮箱。
身份验证:权限范围 READ_EMAILS · 组织权限 view_campaigns
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | string (UUID) | 邮箱,即邮箱账户的 ID。 |
响应
直接返回邮箱对象,结构与列表 data 数组中的单个元素相同。
{
"id": "0c0f1a2b-3c4d-5e6f-7a8b-9c0d1e2f3a4b",
"email": "[email protected]",
"name": "Acme Sales",
"provider": "gmail",
"status": "active",
"campaign_limit": 50,
"min_wait_time": 600,
"warmup": "2026-05-20T00:00:00Z",
"warmup_paused_at": null,
"warmup_pool_type": "premium",
"tracking_domain": "t.acme.com",
"tracking_domain_verified": true,
"auth_state": "passing",
"auth_checked_at": "2026-06-11T03:00:00Z",
"auth_failing_since": null,
"tags": ["outbound"],
"created_at": "2026-05-19T18:00:00Z",
"updated_at": "2026-06-11T09:14:00Z"
}更新邮箱
PATCH /emails/:id
更新邮箱设置,包括显示名称、签名、状态、发送上限、回复地址、预热配置和标签。所有字段均可选,只应用实际提供的字段。
身份验证:权限范围 WRITE_EMAILS · 组织权限 manage_emails
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | string (UUID) | 邮箱 ID。 |
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
name | string | 否 | 发出邮件使用的显示名称。 |
signature_plain | string | 否 | 纯文本签名。 |
signature_html | string | 否 | HTML 签名。 |
signature_sync | boolean | 否 | 在发出邮件中追加签名,与签名来源无关。 |
signature_code | boolean | 否 | 将 HTML 签名视为原始代码。 |
send_as_email | string | 否 | 邮箱使用的、已由服务商验证的发件别名。必须是服务商报告已验证的别名,参阅获取发件身份,或邮箱自身地址;其他值返回 mailbox_send_as_unknown。发送空字符串可恢复使用邮箱地址。仅支持 Gmail。 |
status | string | 否 | active、inactive 或 revoked。 |
campaign_limit | integer | 否 | 跨所有营销活动的冷邮件安全上限,范围 0 至 5000,默认 50。它独立于可配置的可变发送计划目标;新连接邮箱默认启用后者,起始范围为每个当地工作日 28 至 32 封。两者取较低限制,现有邮箱配置不变。 |
min_wait_time | integer | 否 | 两次发送之间的最短秒数。 |
reply_to | string | 否 | 回复地址。 |
timezone | string | 否 | 邮箱自身的 IANA 时区,例如 America/Denver。预热时段、发送行为和工作时间窗口均按此时区计算。发送空字符串可清除:之后邮箱时段按工作区时区解释,未设置则为 UTC;对于营销活动,则只应用活动自身的时间窗口。 |
warmup | boolean | 否 | 启用或停用预热。 |
warmup_base | integer | 否 | 预热起始每日发送量。 |
warmup_max | integer | 否 | 预热每日上限。 |
warmup_increase | integer | 否 | 预热每日发送量递增幅度。 |
warmup_reply_rate | integer | 否 | 需要回复的预热会话百分比。 |
warmup_tag | string | 否 | 应用到预热会话的标签。 |
warmup_start_time | string | 否 | 每日预热窗口开始时间,HH:MM。 |
warmup_end_time | string | 否 | 每日预热窗口结束时间,HH:MM。 |
warmup_days | integer | 否 | 发送预热邮件的星期,以位掩码表示:1 为周一,2 为周二,4 为周三,8 为周四,16 为周五,32 为周六,64 为周日。31 表示周一至周五;0 表示每天。由 Warmbly Cloud 预热的邮箱,会将此字段及其他发送量递增和时段字段发送到 Warmbly Cloud,参阅预热设置。 |
warmup_placement | string | 否 | 预热邮件在邮箱本身中的存放位置:folder(默认)、inbox 或 archive。参阅邮箱预热。 |
warmup_folder | string | 否 | warmup_placement 为 folder 时,用于存放预热邮件的文件夹,Gmail 中对应标签。空字符串使用实例默认值 Warmbly。不得包含文件夹分隔符或控制字符,最长 64 个字符。 |
warmup_retention_days | integer | 否 | 无论 warmup_placement 将邮件保存在何处,Warmbly 删除前在此邮箱保留预热邮件的天数:范围 3 至 3650,或使用 0 遵循实例设置,除非运维人员修改,否则为 30 天。 |
tags | string[] | 否 | 分配给邮箱的标签 ID。 |
{
"name": "Acme Sales (US)",
"status": "active",
"campaign_limit": 40,
"min_wait_time": 720,
"reply_to": "[email protected]",
"warmup_max": 35,
"tags": ["outbound", "us"]
}响应
更新后的邮箱对象,结构与“获取邮箱”相同。
获取发件身份
GET /emails/:id/identity
报告服务商允许该邮箱使用的发件地址、当前所用地址,以及已存签名的来源。仅读取存储状态,不访问服务商,因此调用成本很低。
身份验证:权限范围 READ_EMAILS · 组织权限 view_campaigns
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | string (UUID) | 邮箱 ID。 |
响应
{
"supported": true,
"provider": "gmail",
"mailbox_email": "[email protected]",
"send_as_email": "[email protected]",
"identities": [
{ "email": "[email protected]", "name": "Acme Sales", "is_primary": true, "is_default": true, "verified": true },
{ "email": "[email protected]", "name": "Acme", "is_primary": false, "is_default": false, "verified": true }
],
"synced_at": "2026-09-13T11:02:00Z",
"signature_source": "provider",
"signature_imported_at": "2026-09-13T11:02:00Z"
}Outlook 和 SMTP/IMAP 邮箱不提供发件身份列表,其 supported 为 false,此时 identities 为空,下方刷新接口返回 mailbox_send_as_unsupported。邮箱使用自身地址发送时,send_as_email 为空,这是默认行为。服务商尚未完成验证的别名,其 verified 为 false:仍会列出,但将 send_as_email 设为该别名会被拒绝。存储签名从服务商导入时,signature_source 为 provider;在 Warmbly 中编辑后变为 manual。
刷新发件身份
POST /emails/:id/identity/refresh
从服务商重新读取发件身份地址并保存,可选择同时导入服务商签名。这是唯一会访问服务商的调用,并通过运行该邮箱的工作节点执行,而不是由 API 直接请求,因此服务商看到的该邮箱来源地址始终一致。
同一调用还会清除服务商不再验证的已选发件别名,使移除的别名停止使用,而不是导致每次发送失败。
身份验证:权限范围 WRITE_EMAILS · 组织权限 manage_emails
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | string (UUID) | 邮箱 ID。 |
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
import_signature | boolean | 否 | 同时将邮箱已存签名替换为服务商为当前发件身份配置的签名。默认 false。服务商签名为空时不作修改。 |
{ "import_signature": true }可以完全省略请求体,此时只刷新地址。
不接受 Idempotency-Key:调用读取并原样保存服务商当前状态,因此重复执行会收敛到相同结果。
响应
刷新后的发件身份,结构与“获取发件身份”相同。
仅支持 Gmail。其他服务商返回 400 和 mailbox_send_as_unsupported;签名超过存储上限时,返回 400 和 mailbox_signature_too_large,不保存任何内容。
邮箱当前未在任何工作节点运行时,返回 503 和 mailbox_identity_unavailable:没有可执行调用的机器,不会改变任何内容,之后重试即可。邮箱通常在连接后很快就会分配到节点,因此这一般发生于迁移期间或刚重启后,不是长期状态。
获取跟踪域名
GET /emails/:id/track
返回邮箱保存的跟踪域名状态,以及本实例预期的 CNAME 值。此操作只读,不进行 DNS 查询,因此可在每次渲染时安全调用。需要重新解析时,使用“验证跟踪域名”。
身份验证:权限范围 READ_EMAILS · 组织权限 view_campaigns
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | string (UUID) | 邮箱 ID。 |
响应
{
"tracking_domain": "t.acme.com",
"tracking_domain_verified": false,
"tracking_domain_verified_at": null,
"cname_target": "t.warmbly.com",
"status": "pending",
"message": "t.acme.com has not verified yet. Check it again to see what DNS returns for it right now.",
"tracking_host_unresolvable": false
}更新跟踪域名
PATCH /emails/:id/track
设置或清除邮箱的自定义打开及点击跟踪域名,然后解析一次并记录结果。DNS 记录必须指向本实例的跟踪主机,返回字段为 cname_target,即部署的 TRACKING_DOMAIN,不是固定值。新添加的记录可能尚未传播,因此未匹配时会报告未验证及原因,而非错误。发送空域名可清除自定义域名,回退到共享主机。
保存前会规范化该值,去除协议、路径、末尾点并统一大小写,因此 https://T.Acme.com/ 会保存为 t.acme.com。仍不是纯主机名的值,包括原始 IP、带端口的主机、localhost 或单标签名称,均返回 400 并被拒绝。
发送时只使用已验证域名。尚未验证时,打开和点击通过共享跟踪主机处理,因此待生效记录不会阻止发送。
身份验证:权限范围 WRITE_EMAILS · 组织权限 manage_emails
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | string (UUID) | 邮箱 ID。 |
domain | query | string | 自定义跟踪子域名,例如 t.acme.com。留空可清除。 |
响应
{
"tracking_domain": "t.acme.com",
"tracking_domain_verified": true,
"tracking_domain_verified_at": "2026-06-11T09:20:00Z",
"cname_target": "t.warmbly.com",
"status": "verified",
"message": "t.acme.com points at t.warmbly.com.",
"observed": "t.warmbly.com",
"tracking_host_unresolvable": false
}记录尚未解析到跟踪主机时,tracking_domain_verified_at 为 null。
| 字段 | 说明 |
|---|---|
cname_target | 应填入 CNAME 的值。部署没有跟踪主机时为空,此时无法完成验证。 |
status | verified、unset、no_target、not_found、wrong_target、lookup_error,或表示存储状态尚未重新解析的 pending。 |
message | 用一句话说明 status,可安全展示给最终用户。 |
observed | DNS 返回值与目标不同时,报告实际返回内容。 |
tracking_host_unresolvable | 记录正确,但本实例自身的跟踪主机没有 DNS 记录时为 true,因此不会记录任何跟踪数据。这是运维问题,不是调用者问题。 |
若域名服务商对 CNAME 记录进行扁平化,则改为匹配地址验证,status 仍为 verified。
验证跟踪域名
POST /emails/:id/track/verify
重新解析邮箱已保存的跟踪域名并记录结果,不修改域名本身。这样已完成传播的记录即可开始使用,无需再次保存。
后端还会每小时执行自定义跟踪域名重新解析任务,每个域名每天最多检查一次,因此此端点用于立即触发检查,并非唯一方式。临时解析失败不会撤销已验证域名。
有意要求写入权限范围:记录验证结果会使真实链接开始通过自定义主机路由。
身份验证:权限范围 WRITE_EMAILS · 组织权限 manage_emails
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | string (UUID) | 邮箱 ID。 |
响应
结构与“更新跟踪域名”相同。
启动邮箱预热
POST /emails/:id/warmup/start
为邮箱启用预热。从暂停恢复时会保留发送量递增进度,并立即初始化预热任务链,无需等待下一轮协调检查。投递位置测试种子收件箱永远不会预热,因此启动或恢复其预热会返回 409 和 mailbox_is_seed。
身份验证:权限范围 WRITE_EMAILS · 组织权限 manage_emails
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | string (UUID) | 邮箱 ID。 |
响应
更新后的邮箱对象,反映新的预热状态。
{
"id": "0c0f1a2b-3c4d-5e6f-7a8b-9c0d1e2f3a4b",
"email": "[email protected]",
"warmup": "2026-06-11T09:25:00Z",
"warmup_paused_at": null,
"warmup_pool_type": "premium"
}暂停邮箱预热
POST /emails/:id/warmup/pause
暂停预热而不丢失发送量递增进度。之后启动时,从相同的每日发送量继续。
身份验证:权限范围 WRITE_EMAILS · 组织权限 manage_emails
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | string (UUID) | 邮箱 ID。 |
响应
更新后的邮箱对象。暂停邮箱的 warmup_paused_at 非 null。
{
"id": "0c0f1a2b-3c4d-5e6f-7a8b-9c0d1e2f3a4b",
"warmup": "2026-06-11T09:25:00Z",
"warmup_paused_at": "2026-06-11T11:00:00Z"
}恢复邮箱预热
POST /emails/:id/warmup/resume
恢复暂停的预热,将递增起算时间向后移动,使进度从暂停处继续,并立即重新初始化预热任务链。
身份验证:权限范围 WRITE_EMAILS · 组织权限 manage_emails
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | string (UUID) | 邮箱 ID。 |
响应
更新后的邮箱对象,其中 warmup_paused_at 已清除。
停止邮箱预热
POST /emails/:id/warmup/stop
完全停用预热,并清除发送量递增进度。与暂停不同,之后启动会重新开始递增计划。
身份验证:权限范围 WRITE_EMAILS · 组织权限 manage_emails
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | string (UUID) | 邮箱 ID。 |
响应
更新后的邮箱对象,预热已停用。
{
"id": "0c0f1a2b-3c4d-5e6f-7a8b-9c0d1e2f3a4b",
"warmup": null,
"warmup_paused_at": null
}暂停邮箱发送
POST /emails/:id/hold
暂停邮箱参与营销活动发送,直到解除限制,预热仍继续运行。这是所有者主动决定的限制,自动休息和恢复逻辑不会操作被暂停的邮箱。无请求体,且具有幂等性,因此不接受 Idempotency-Key;对已暂停邮箱再次暂停,会返回当前状态。
身份验证:权限范围 WRITE_EMAILS · 组织权限 manage_emails
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | string (UUID) | 邮箱 ID。 |
响应
邮箱的冷邮件轮换状态。
{
"state": "reserve",
"since": "2026-08-28T10:15:00Z",
"reason": "held back by its owner"
}解除邮箱暂停限制
POST /emails/:id/release
将手动暂停或休息中的邮箱重新交给自动管理。通常转为 active;如果预热正在运行,且仍报告邮箱被隔离或封禁,则直接转为 resting,因此解除限制不会让预热池已移除的邮箱发送冷邮件。watch 或 throttled 邮箱转为 active,并按降低后的发送量发送。没有运行预热的休息中邮箱,无需等待健康信号,会转为 active。无请求体,且具有幂等性;对已启用邮箱解除限制,会返回当前状态。
身份验证:权限范围 WRITE_EMAILS · 组织权限 manage_emails
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | string (UUID) | 邮箱 ID。 |
响应
邮箱的冷邮件轮换状态。
{
"state": "active",
"since": "2026-08-28T11:40:00Z",
"reason": "released by its owner"
}检查域名身份验证
GET /emails/:id/auth-check
按需验证邮箱发件域名的 SPF、DKIM 和 DMARC。身份验证对齐是批量发件的硬性要求,也常是邮件送达问题的隐蔽原因;此接口让你无需离开控制台即可确认域名配置是否正确。
只读:报告当前 DNS 结果,不改变邮箱已保存的 auth_state。
身份验证:权限范围 READ_EMAILS · 组织权限 view_campaigns
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | string (UUID) | 邮箱 ID。域名从邮箱地址中提取。 |
响应
{
"domain": "mail.acme.com",
"spf_found": true,
"spf_record": "v=spf1 include:_spf.google.com ~all",
"dkim_found": true,
"dkim_status": "found",
"dkim_selectors": ["google"],
"dmarc_found": true,
"dmarc_policy": "quarantine",
"dmarc_domain": "acme.com",
"dmarc_inherited": true,
"reserved": false,
"lookup_error": false,
"all_aligned": true,
"summary": "SPF, DKIM and DMARC all present (DMARC policy: quarantine), inherited from acme.com"
}对应记录未找到时,会省略 spf_record、dkim_selectors、dmarc_policy 和 dmarc_domain。
dkim_status 为 found 或 undetermined,应通过此字段判断 DKIM。dkim_found 仅能提供肯定证据:DKIM 密钥位于域名所有者选定的选择器中,DNS 无法列出域名下的所有选择器,因此 false 只表示探测过的选择器没有密钥响应,不能证明域名没有 DKIM。不要将其展示为缺少记录。检查除使用默认选择器集合外,还会根据域名自身 SPF 和 MX 记录推导候选选择器,这些记录会标明邮件处理服务商;如果服务商为每个账户生成随机选择器,无论域名配置多么正确,结果都可能保持为 undetermined。DKIM 永远不会影响 auth_state。
发件域名没有自己的 DMARC 记录,但被其组织域名的策略覆盖时,dmarc_inherited 为 true,专用发件子域通常采用此方式。此时 dmarc_domain 标明实际读取记录的域名;记录发布了 sp= 时,dmarc_policy 报告该值。SPF 不会继承,必须在精确的发件域名上发布。
对于实例解析器未返回的记录,会先向域名自身的权威名称服务器确认,然后才可能将 spf_found 或 dmarc_found 设为 false,避免过期的负缓存,或丢弃 TXT 响应的解析器,将已配置域名误报为缺少记录。
实例解析器或域名名称服务器无法给出 DNS 回答时,lookup_error 为 true;对于按定义无法解析的特殊用途域名(.test、.invalid、.localhost、.example、.local),reserved 为 true。任一情况都表示域名无法评估,因此记录为 unknown,而非 failing。
记录域名身份验证检查
POST /emails/:id/auth-check
运行相同检查,并为该发件域名上的所有有效邮箱记录结果,因为身份验证属于域名属性,而不是单个邮箱属性。
这也是解除邮箱发送限制的方式:在域名注册商处修复 DNS 记录,调用此接口,冷邮件发送和预热便可在下一次计划发送时恢复,无需等待每日后台检查。
记录结果会解除发送限制,因此即使只读取公开 DNS,也需要写入权限范围。
身份验证:权限范围 WRITE_EMAILS · 组织权限 manage_emails
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | string (UUID) | 邮箱 ID。域名从邮箱地址中提取。 |
无请求体。无需 Idempotency-Key:结果完全根据公开 DNS 推导,没有调用者输入,因此重复调用会收敛到相同存储状态。
响应
与 GET /emails/:id/auth-check 相同。
验证邮箱地址
POST /emails/verify
按需验证单个邮件地址,依次检查语法、MX、SMTP RCPT 探测和全收地址检测。这是发送前验证,在工作节点实际发送前确认地址可投递,而不是通过硬退信才得知问题。探测从后端的非发件 IP 执行,绝不使用工作节点 IP。
身份验证:权限范围 READ_EMAILS · 组织权限 view_campaigns
地址可放在 JSON 请求体或 email 查询参数中,请求体优先。
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
email | string | 否 | 要验证的地址。未设置 email 查询参数时必填。 |
{
"email": "[email protected]"
}响应
{
"email": "[email protected]",
"status": "valid",
"reason": "accepted by recipient mail server",
"is_catch_all": false,
"has_mx": true,
"checked_at": "2026-06-11T09:30:00Z"
}status 为 valid、risky、invalid 或 unknown。地址缺失或为空时,返回 400 错误封装。
获取预热封禁状态
GET /emails/:id/warmup/ban-status
返回调用者工作区的邮箱是否被共享预热池封禁、原因及能否申诉,用于控制台封禁横幅。邮箱不属于工作区时返回 404。
身份验证:权限范围 READ_EMAILS · 组织权限 view_campaigns
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | string (UUID) | 邮箱 ID。 |
响应
{
"email_account_id": "0c0f1a2b-3c4d-5e6f-7a8b-9c0d1e2f3a4b",
"blocked": true,
"health_state": "quarantined",
"reason": "complaint rate 0.15% exceeded quarantine threshold",
"blocked_at": "2026-06-09T14:00:00Z",
"blocked_until": "2026-06-16T14:00:00Z",
"can_appeal": true,
"pending_appeal": false
}邮箱未被封禁时,省略 reason、blocked_at 和 blocked_until。health_state 反映邮箱滚动预热健康状态,例如 healthy、watch、throttled、quarantined 或 blocked。
提交预热封禁申诉
POST /emails/:id/warmup/appeal
为调用者工作区的邮箱提交预热封禁申诉,并附上原因。任何具有 manage_emails 的成员都可申诉,不限于连接该邮箱的成员。邮箱不属于工作区时返回 404。
身份验证:权限范围 WRITE_EMAILS · 组织权限 manage_emails
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | string (UUID) | 邮箱 ID。 |
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
reason | string | 否 | 申诉说明。 |
{
"reason": "Authentication is fixed and the high-bounce list has been removed."
}响应
{
"appeal_id": "5e6f7a8b-9c0d-1e2f-3a4b-5c6d7e8f9a0b"
}删除邮箱
DELETE /emails/:id
断开并删除邮箱。邮箱会从所有预热池移除,并广播账户断开连接事件。
只有两个事后无法补救的步骤先成功,才会移除记录:通知同步该邮箱的机器释放邮箱,以及解除邮箱的 Warmbly Cloud 关联。后者会将已加入预热池邮箱的存储凭据从池中移除,并将云端管理的镜像归还云端工作区。任一步失败都会将邮箱恢复原状,可安全重试。如果随后记录本身删除失败,邮箱仍保留,但关联已解除,因此预热会回到本实例,直到重试删除。
身份验证:权限范围 WRITE_EMAILS · 组织权限 manage_emails
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | string (UUID) | 邮箱 ID。 |
响应
204 No Content,响应体为空。
| 状态 | code | 含义 |
|---|---|---|
409 | mailbox_cloud_unenroll_failed | 无法解除邮箱的 Warmbly Cloud 关联。参阅错误码 |
503 | mailbox_worker_unreachable | 无法通知同步邮箱的机器释放该邮箱。参阅错误码 |
通过邮箱发送邮件
POST /emails/:id/send
从指定邮箱发送一次性邮件。发送通过邮箱分配的工作节点调度和执行。使用 send_mode 选择调度方式。
身份验证:权限范围 SEND_CAMPAIGNS · 组织权限 send_campaigns。要求组织处于有效状态。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | string (UUID) | 发件邮箱 ID。 |
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
to | string[] | 是 | 收件地址。 |
cc | string[] | 否 | 抄送地址。 |
bcc | string[] | 否 | 密送地址。 |
subject | string | 是 | 邮件主题。 |
body_html | string | 否 | HTML 正文。 |
body_plain | string | 否 | 纯文本正文。 |
in_reply_to | string[] | 否 | 此邮件所回复的邮件 ID。 |
thread_id | string | 否 | 此邮件要归入的会话 ID。 |
send_mode | string | 否 | instant(默认)、smart(邮箱调度器的下一个发送空档),或 scheduled(使用 scheduled_at)。 |
scheduled_at | string (RFC 3339) | 否 | send_mode 为 scheduled 时必填,且必须是未来时间。 |
{
"to": ["[email protected]"],
"subject": "Quick question about your rollout",
"body_html": "<p>Hi Jane, ...</p>",
"body_plain": "Hi Jane, ...",
"send_mode": "smart"
}响应
{
"task_id": "9a0b1c2d-3e4f-5a6b-7c8d-9e0f1a2b3c4d",
"scheduled_at": "2026-06-11T09:45:00Z",
"send_mode": "smart"
}task_id 标识已入队的发送任务。scheduled_at 是解析后的发送时间:instant 为立即,smart 为下一个空档,scheduled 为请求的时间。
收件人直接或因所属域名命中工作区禁止发送名单时,返回 400,包含 code: "recipient_suppressed" 和指明该地址的 message,不会将任何内容加入发送队列。
连接邮箱(初始设置)
三个初始接入路由用于连接新邮箱,第四个将现有邮箱迁移到应用密码方式。它们仅限会话,API 密钥不可用,因为通过 SPA 弹窗流程写入由用户密钥加密的服务商刷新令牌。
开始 OAuth 授权
POST /emails/onboarding/oauth/start
为 Gmail 或 Outlook 邮箱启动 OAuth 往返流程,返回服务商授权 URL,以及需在回调中传回的不透明 state。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
provider | string | 是 | gmail 或 outlook。除非 GET /auth/config 报告 gmail_oauth_connect: true,否则 gmail 会返回 403 mailbox_gmail_oauth_disabled 并被拒绝;新 Gmail 邮箱改为使用应用密码通过连接 SMTP/IMAP接入。重新授权现有 Gmail 邮箱不受此限制。 |
login_hint | string | 否 | 在服务商登录页面预先选中的地址。 |
return | string | 否 | 浏览器控制台发起往返流程时使用 web。登录窗口没有 opener 时,会返回控制台 /oauth-return 页面,而非 warmbly:// 协议地址。原生应用应省略。 |
{
"provider": "gmail"
}响应:
{
"url": "https://accounts.google.com/o/oauth2/auth?...",
"state": "n0nc3-opaque-state"
}对于 outlook,响应还包含 admin_consent_url,即针对同一组委派权限、在整个租户范围生效的 Microsoft 管理员同意页面。Microsoft 登录返回 Need admin approval(需要管理员批准) 时,应将该地址交给组织管理员;批准后,组织中的所有邮箱都可登录。页面会返回 API 回调页,展示结果,不向 opener 返回任何内容。
用户批准后,服务商重定向到 API 回调页面,将 code 和 state 交回客户端:网页 opener 通过 postMessage 接收。没有 opener 时,以 return: "web" 发起的往返流程会前往控制台 /oauth-return 页面,由其将结果传给等待中的控制台标签页;这会发生在浏览器先阻止登录弹窗、用户随后允许的情况下。其他没有 opener 的流程,例如原生应用内浏览器会话,会重定向到 warmbly://email-oauth?provider=...&code=...&state=...&error=...。无论哪种情况,客户端随后都调用“完成 OAuth”。
完成 OAuth 授权
POST /emails/onboarding/oauth/finish
使用服务商返回的授权码和状态完成 OAuth 往返流程,然后创建邮箱。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
code | string | 是 | 服务商返回的授权码。 |
state | string | 是 | 启动时返回的 state。 |
{
"code": "4/0Ax...",
"state": "n0nc3-opaque-state"
}响应:201 Created,包含新邮箱对象,结构与“获取邮箱”相同。
连接 SMTP/IMAP
POST /emails/onboarding/smtp-imap
通过一次调用连接 SMTP/IMAP 邮箱。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
email | string | 是 | 邮箱地址。 |
name | string | 否 | 显示名称。 |
smtp | object | 是 | SMTP 凭据:username、password、host、port、security。 |
imap | object | 是 | IMAP 凭据:username、password、host、port、security。 |
security 为 tls(隐式 TLS,从第一个字节开始加密)、starttls(明文问候后在连接内升级加密)或 none(不加密)。此字段可选,省略时根据端口判断:SMTP 465 和 IMAP 993 使用 tls,SMTP 587 和 IMAP 143 使用 starttls。绝不会根据端口推断 none,只有明确请求时才采用。使用非标准配置时,例如 2525 端口的邮件提交中继,应明确设置模式。接受 1 至 65535 的任意端口。
只有邮件服务器与工作节点位于同一机器时才接受 none,即自托管实例,且 host 为 localhost、127.0.0.0/8 范围内的地址,或 ::1。此模式用于 Proton Bridge 等本地中继,它们在回环接口提供明文 IMAP 和 SMTP;参阅本地邮件中继。仅仅解析到回环地址的主机名会被拒绝;托管产品会以 400 拒绝请求,工作节点也会在建立连接时再次检查对端。其他情况下 TLS 均为必需。
{
"email": "[email protected]",
"name": "Acme Sales",
"smtp": {
"username": "[email protected]",
"password": "app-specific-password",
"host": "smtp.acme.com",
"port": 587,
"security": "starttls"
},
"imap": {
"username": "[email protected]",
"password": "app-specific-password",
"host": "imap.acme.com",
"port": 993,
"security": "tls"
}
}响应:201 Created,包含新邮箱对象。
切换到应用专用密码
POST /emails/onboarding/app-password/:id
将通过逐邮箱 Google 登录连接、且该方式正在停用的邮箱,迁移到使用 Google 应用密码的 Gmail IMAP 和 SMTP。仅限会话,需要 manage_emails 组织权限。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
app_password | string | 是 | 从 myaccount.google.com/apppasswords 获取的 16 个字母应用密码。忽略空格。 |
{
"app_password": "abcd efgh ijkl mnop"
}保存任何内容前,会向 imap.gmail.com 和 smtp.gmail.com 验证密码;拒绝时使用与连接 SMTP/IMAP相同的错误码,例如 mailbox_auth_refused,不修改任何内容。成功后,邮箱原地变为 smtp_imap,auth_method 为 app_password:ID、已导入邮件、营销活动、预热和设置均保留,同步继续获取新邮件。
响应:200 OK,包含邮箱对象。邮箱未使用逐邮箱 Google 登录时,包括已经迁移的邮箱,返回 409 mailbox_not_google_signin,因此重复调用不会改变任何内容。值不是 16 个字母时,返回 400 app_password_invalid。
连接多个邮箱
另有四组路由用于批量连接和配置邮箱。与初始接入一样,它们仅限会话,API 密钥不可用,因为会携带密码或访问整个域名的邮件;每组都需要 manage_emails 组织权限:
| 路由 | 用途 |
|---|---|
/emails/imports | 从文件或粘贴列表在后台导入邮箱 |
/emails/vendors | 连接邮箱供应商账户并导入其邮箱 |
/emails/grants | 通过管理员授权连接整个 Google Workspace 域名 或 Microsoft 365 组织,并将使用逐邮箱登录的邮箱迁移到该授权;GET /emails/grants/migration 列出仍使用 Google 登录的邮箱。记录、使用和移除授权还需要近期身份确认 |
/emails/domains | 为每个发件域名设置跟踪主机和根域重定向,可直接设置,也可通过持有域名的邮箱供应商设置 |
所有路由及其请求体和重试行为均列于仅支持 JWT。