营销活动
创建营销活动和邮件序列,管理发件人、A/B 测试版本、附件、发送量递增与跟踪设置,执行发送前检查,并开始或停止发送。
营销活动是 Warmbly 中的冷邮件营销单元。每个营销活动包含发送规则、时间安排、发件邮箱池,以及按顺序排列的邮件序列步骤(邮件或操作节点)。这些端点涵盖营销活动的增删改查、高级营销设置覆盖、各步骤的 A/B 变体、附件、显式发件邮箱池、发送前检查和测试发送、启动和停止、活动日志、营销活动级跟踪域名验证、嵌套邮件序列编辑器、模板预览工具及 AI 写作助手。
所有错误均使用 错误码 中说明的统一 {error, message, code, request_id} 响应结构。身份验证和权限范围模型分别见 身份验证 和 权限。
daily_limit 分别作用于每个邮箱,而非营销活动的发送总量。营销活动的发送额度按 UTC 自然日计算;timezone 则用于控制发送时段。每个邮箱还受到自身上限、共用该邮箱的其他营销活动、共用的每日及滚动收件人数限制,以及调度器安全限制的约束。分发预留只计数一次;唤醒营销活动后未发送邮件,则不会消耗额度。详见 营销活动发送限制。
列出营销活动
GET /campaigns
搜索组织的营销活动并分页浏览。权限范围 READ_CAMPAIGNS · 组织权限 view_campaigns。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
q | query | string | 按营销活动名称进行自由文本筛选。可选。 |
folder | query | string | 限定为单个文件夹 ID。可选。 |
status | query | string | 按状态类别筛选:draft、active、paused(匹配所有暂停状态变体)或 completed。其他值均返回 400。可选。 |
cursor | query | string | 上一页 pagination.next_cursor 中的不透明游标。可选。 |
limit | query | string | 每页条数。可选。 |
响应
包含 data 和 pagination 的响应结构。next_cursor 是继续分页的起始营销活动 ID(最后一页为 null);total 是符合当前 q/folder/status 筛选条件的数量。
{
"data": [
{
"id": "8f1d6b2e-2b7a-4c9e-9a1f-0e6d4c3b2a10",
"user_id": "a2c4...",
"organization_id": "11111111-2222-3333-4444-555555555555",
"name": "Q3 outbound",
"description": "",
"status": "active",
"stop_on_reply": true,
"open_tracking": true,
"link_tracking": true,
"text_only": false,
"daily_limit": 50,
"unsubscribe_header": true,
"risky_emails": false,
"cc": [],
"bcc": [],
"start_date": null,
"end_date": null,
"timezone": "",
"effective_timezone": "America/New_York",
"days": 62,
"start_time": "09:00",
"end_time": "17:00",
"schedule_windows": [[],[{"start":540,"end":1020}],[],[],[],[],[]],
"email_tags": ["sales"],
"folders": [],
"contact_order_by": "created_at",
"contact_order_dir": "asc",
"sender_strategy": "tags",
"rotation_mode": "round_robin",
"ramp_enabled": false,
"ramp_start": 0,
"ramp_increment": 0,
"ramp_ceiling": 0,
"ramp_level": 0,
"esp_match_mode": "off",
"max_new_leads_per_day": 0,
"prioritize_new_leads": false,
"entry_delay_minutes": 0,
"continuous": false,
"idle_since": null,
"tracking_domain": "",
"tracking_domain_verified": false,
"utm_tracking": true,
"utm_source": "",
"utm_medium": "",
"utm_campaign": "",
"updated_at": "2026-06-10T12:00:00Z",
"created_at": "2026-06-01T09:00:00Z"
}
],
"pagination": {
"total": 12,
"next_cursor": "c1_b3BhcXVlLWN1cnNvcg",
"has_more": true
}
}营销活动概览
GET /campaigns-overview
返回组织各状态类别的计数及各文件夹的总数,供营销活动浏览界面使用。paused 汇总所有暂停状态变体(paused、paused_no_accounts、paused_trial_expired)。路径不包含营销活动 ID,因此与 /campaigns 同级,而不位于其下。权限范围 READ_CAMPAIGNS · 组织权限 view_campaigns。
响应
{
"total": 12,
"active": 3,
"paused": 2,
"draft": 4,
"completed": 3,
"folders": [
{ "folder_id": "6b9c1c8e-1f2a-4d3b-8c7e-9a0b1c2d3e4f", "total": 5 }
]
}估算发送安排
POST /campaigns-estimate
在创建营销活动之前,基于发件邮箱池预测目标受众的发送计划。系统按调度器自身的规则逐日模拟营销活动:各邮箱上限及营销活动限制、预热达标后的递增发送上限、工作区风险等级、健康等级及暂停限制、域名身份验证、冷邮件轮换、发送行为配置、套餐每日额度、同一批邮箱中其他营销活动已有的发送量,以及发送时段和其中的发送间隔。仍在预热的邮箱所发送的预热邮件也共用这些间隔,因此同样计入。在邮件序列中,到期的跟进邮件先于新联系人的首封邮件发送,且总量预测假定没有人回复。本操作不写入任何内容,因此无需 Idempotency-Key。控制台的新建营销活动流程将此结果显示为启动计划。权限范围 READ_CAMPAIGNS · 组织权限 view_campaigns。
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
segment_ids | string[] | 是 | 构成目标受众的联系人分群(最多 20 个)。同一联系人出现在多个分群中时仅计数一次。发送 [] 可仅预测邮箱池本身:返回其中的邮箱及容量,不提供完成日期。 |
email_tag_ids | string[] | 否 | 用于确定邮箱池的邮箱标签。省略或发送 [] 时,使用工作区的所有活跃邮箱。 |
daily_limit | integer | 否 | 可配置的营销活动单邮箱发送上限,范围为 3 至 5000(默认为 50),并非每日目标量。新邮箱默认另按本地工作日规划每天 28 至 32 封;两项限制中较低者生效。 |
days | integer (0-127) | 否 | 发送日的星期位掩码,第 0 位代表星期一。默认为工作日。 |
timezone | string | 否 | 解释发送日和时段时使用的 IANA 时区。省略或发送 "" 时,使用工作区时区(未设置时为 UTC)。 |
start_date | string (RFC 3339) | 否 | 开始发送的时间。省略表示现在。 |
start_time | string | 否 | 每日发送时段的起始时间(HH:MM)。默认为 08:00,即新营销活动采用的时段起点。 |
end_time | string | 否 | 每日发送时段的结束时间(HH:MM)。默认为 18:00。结束时间等于或早于开始时间时返回 400。 |
step_waits | integer[] | 否 | 按顺序提供各封跟进邮件的 wait_after,单位为天(0 至 365)。单封邮件时可省略。最多 30 项。 |
campaign_id | string | 否 | 预测此工作区内已保存的营销活动。活动中并非由关联分群加入的潜在客户,会与 segment_ids 一起计入 recipients(每位联系人只计数一次;由分群关联加入的潜在客户,仅在该分群仍位于 segment_ids 中时计入,因为取消关联会将其移出)。未提供的设置(daily_limit、days、timezone、发送时段,包括按天设置的时段)均取自营销活动;若活动使用逐个选定的邮箱发送,则采用这些邮箱。属于其他工作区的营销活动返回 404。 |
响应
目标受众为空、邮箱池没有发送容量,或发送需要超过两年时,sending_days 和 estimated_finish_at 为 null。daily_capacity 和 remaining_today 分别为邮箱池今天的上限和今天尚未使用的发送量。steady_capacity 表示所有邮箱预热达标后每个发送日的容量,于 full_capacity_at 达到(若已达到,则为 null)。日期采用营销活动时区的午夜时间。
| 字段 | 说明 |
|---|---|
steps, total_sends | 每位联系人的邮件数,以及 recipients 乘以 steps。 |
first_touch_finish_at | 最后一位联系人收到首封邮件的日期。 |
ramping, held | 预热达标后的发送上限仍在递增的邮箱数量,以及今天不发送邮件的邮箱数量。 |
warmup | {mailboxes, per_day}:与营销活动同时运行的预热邮件。 |
other_campaigns_per_day | 过去一周中,邮箱池内各邮箱为其他营销活动发送的日均邮件量。 |
bottleneck | 首个完整发送日中导致发送量损失最多的限制:campaign_limit、warmup_graduation、spacing、other_campaigns、health、held、workspace_risk、org_daily_limit 或 sending_behavior。若限制来自邮箱自身上限,则为空。 |
timeline | 从开始日期起最多 120 天:date、sending_day、capacity、sends、first_emails、follow_ups、warmup。 |
senders | 最多 200 个邮箱:id、email、provider、state(ready、ramping、throttled、health_hold、domain_auth、resting、no_worker)、first_day_cap、steady_cap、warmup_per_day、full_cap_at。 |
{
"recipients": 1000,
"mailboxes": 4,
"daily_capacity": 80,
"remaining_today": 60,
"sending_days": 14,
"estimated_finish_at": "2026-10-16T00:00:00+02:00",
"steps": 2,
"total_sends": 2000,
"first_touch_finish_at": "2026-10-09T00:00:00+02:00",
"steady_capacity": 180,
"full_capacity_at": "2026-10-06T00:00:00+02:00",
"ramping": 4,
"held": 0,
"warmup": { "mailboxes": 4, "per_day": 20 },
"other_campaigns_per_day": 0,
"bottleneck": "warmup_graduation",
"timeline": [
{ "date": "2026-09-28", "sending_day": true, "capacity": 60, "sends": 60, "first_emails": 60, "follow_ups": 0, "warmup": 20 }
],
"senders": [
{ "id": "5d0c…", "email": "[email protected]", "provider": "gmail", "state": "ramping", "first_day_cap": 20, "steady_cap": 45, "warmup_per_day": 5, "full_cap_at": "2026-10-06T00:00:00+02:00" }
]
}创建营销活动
POST /campaigns
创建营销活动。仅 name 必填,其他字段均可选,且只应用已提交的字段(向导会一次提交全部设置,简单弹窗则可以只提交 {name, description},并采用合理的默认值)。权限范围 WRITE_CAMPAIGNS · 组织权限 manage_campaigns。
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
name | string | 是 | 营销活动名称。 |
description | string | 否 | 自由文本说明。 |
stop_on_reply | boolean | 否 | 联系人回复后停止向其发送。 |
open_tracking | boolean | 否 | 插入打开跟踪像素。 |
link_tracking | boolean | 否 | 通过跟踪票据服务重写链接。每个链接获得独立票据,因此点击可归因到具体链接。 |
utm_tracking | boolean | 否 | 发送时为每个链接添加 utm_source、utm_medium、utm_campaign 和各链接独立的 utm_content。默认为 false。保留链接中已有的值。 |
utm_source | string | 否 | 覆盖默认值 warmbly。为空时使用默认值。最多 128 个字符。 |
utm_medium | string | 否 | 覆盖默认值 email。为空时使用默认值。 |
utm_campaign | string | 否 | 覆盖默认值(由营销活动名称生成的 slug)。为空时使用默认值。 |
text_only | boolean | 否 | 仅发送纯文本:不包含 HTML 部分,并关闭打开和点击跟踪,无论相应开关如何设置。 |
daily_limit | integer | 否 | 营销活动中每个邮箱的每日发送上限,范围为 3 至 5000。 |
unsubscribe_header | boolean | 否 | 添加 RFC 8058 一键退订邮件头。 |
risky_emails | boolean | 否 | 允许向有风险或未经验证的地址发送。 |
cc | string[] | 否 | 静态抄送列表,应用于发给每位潜在客户的每封邮件。若地址被禁止发送、是该潜在客户本人的地址,或曾在此营销活动中退信,则不会出现在该封邮件的抄送列表中。仅为某一位潜在客户添加抄送时,参见 设置潜在客户的抄送地址。 |
bcc | string[] | 否 | 静态密送列表,采用相同的筛选规则。 |
start_date | string (RFC 3339), nullable | 否 | 最早发送时间。必须是今天或之后;省略或发送 null 时,营销活动进入活跃状态后即可开始。 |
end_date | string (RFC 3339), nullable | 否 | 最晚发送时间。必须位于未来;省略或发送 null 表示营销活动不设结束时间。 |
timezone | string | 否 | 时间安排使用的 IANA 时区。省略或发送 "" 时跟随工作区时区,每次读取时重新确定(未设置时为 UTC)。响应通过 effective_timezone 返回实际使用的时区。 |
days | integer (0-127) | 否 | 旧版星期位掩码(由 schedule_windows 替代)。 |
start_time | string | 否 | 旧版每日开始时间(HH:MM)。 |
end_time | string | 否 | 旧版每日结束时间(HH:MM)。 |
schedule_windows | array | 否 | 按天设置的发送时段:由 7 个数组组成,按星期索引(星期日为 0),每项为以当天分钟数表示的 {start, end} 区间。非空时优先于 days、start_time 和 end_time。 |
email_tag_ids | string[] | 否 | 用于确定发件邮箱池的邮箱标签 ID(标签策略)。 |
folder_ids | string[] | 否 | 营销活动所属的文件夹 ID。 |
sender_strategy | string | 否 | tags(默认)或 explicit。tags 根据 email_tag_ids 确定邮箱池;若未设置标签且未指定显式发件邮箱,则回退到工作区的所有活跃邮箱。explicit 使用 senders 中的邮箱,以及同时设置的 email_tag_ids。此策略绝不会回退到所有活跃邮箱,因此邮箱池变空时,营销活动会停留在 paused_no_accounts,而不会将范围扩大到整个工作区。 |
rotation_mode | string | 否 | 发送量在所选邮箱之间的分配方式。 |
senders | object[] | 否 | 显式策略的邮箱池(参见下方的发件邮箱输入)。 |
ramp_enabled | boolean | 否 | 启用营销活动级每日发送量递增。 |
ramp_start | integer | 否 | 发送量递增的起始量。 |
ramp_increment | integer | 否 | 每天递增的发送量。 |
ramp_ceiling | integer | 否 | 发送量递增上限(不会超过单邮箱上限)。 |
esp_match_mode | string | 否 | off、prefer 或 strict。 |
max_new_leads_per_day | integer | 否 | 新潜在客户限流值;0 表示不限制。 |
prioritize_new_leads | boolean | 否 | 在各发送时段中优先处理新潜在客户。 |
entry_delay_minutes | integer | 否 | 联系人加入营销活动后,其首封邮件等待的时长。0(默认值)表示时间安排和邮箱限制允许时立即发送;最大值为 129600(90 天)。跟进邮件间隔不受影响,仍由各步骤的 wait_after 决定。 |
continuous | boolean | 否 | 持续接收新潜在客户:潜在客户全部处理完毕时,营销活动保持 active 并等待,而不是结束。关联会加入潜在客户的分群、表单或自动化流程会启用此设置;启动一个所有潜在客户都已完成的营销活动也会启用它。默认为 false。 |
tracking_domain | string | 否 | 营销活动级跟踪域名(验证通过后才会使用)。 |
steps | object[] | 否 | 按顺序排列的初始邮件序列步骤(参见下方的创建邮件序列输入)。各步骤依次连接:每一步无条件进入下一步,并等待该步骤 wait_after 指定的天数。第一步的 wait_after 默认为 0,跟进步骤默认为 3。未提供 subject、或主题与当前会话相同的步骤,默认采用 thread_reply: true,作为回复发送并沿用会话主题;使用独立主题的步骤默认为 false,会开启新会话。显式设置 thread_reply 可覆盖上述任一默认行为。 |
variants | object[] | 否 | 第一步的 A/B 变体(结构与创建 A/B 变体相同)。 |
advanced_overrides | object | 否 | 高级营销设置覆盖,参见 高级设置。 |
schedule_windows 也可提供为包含 7 个元素的数组(按 time.Weekday 索引,星期日为 0),各元素为 {start, end} 分钟区间。非空时优先于 days/start_time/end_time。
{
"name": "Q3 outbound",
"description": "Founders in fintech",
"stop_on_reply": true,
"open_tracking": true,
"link_tracking": true,
"daily_limit": 40,
"unsubscribe_header": true,
"timezone": "America/New_York",
"email_tag_ids": ["3b0a...", "9d2c..."],
"sender_strategy": "tags",
"rotation_mode": "round_robin",
"ramp_enabled": true,
"ramp_start": 10,
"ramp_increment": 2,
"ramp_ceiling": 40,
"steps": [
{ "name": "Step 1", "subject": "Quick question, {{first_name}}", "body_plain": "Hi {{first_name}}...", "wait_after": 0 },
{ "name": "Step 2", "subject": "", "body_plain": "Just bumping this...", "wait_after": 3 }
]
}响应
创建的 Campaign 对象(结构与 列表 的 data 数组中的单个元素相同)。
获取营销活动
GET /campaigns/:id
按 ID 获取单个营销活动。权限范围 READ_CAMPAIGNS · 组织权限 view_campaigns。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
响应
一个 Campaign 对象(参见 列表 的结构)。
更新营销活动
PATCH /campaigns/:id
更新任意部分营销活动字段。省略的字段保持不变。显式发件邮箱列表通过 替换发件邮箱 编辑;此 PATCH 仅包含 sender_strategy/rotation_mode 切换设置。权限范围 WRITE_CAMPAIGNS · 组织权限 manage_campaigns。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
请求体
所有字段均可选。标量字段使用可空指针,因此提交的任何字段都会应用。timezone 接受 "",使营销活动跟随工作区时区。start_date 和 end_date 还接受显式 null 以清除已保存的日期:start_date 为空表示“立即开始”,end_date 为空表示“不设结束时间”。对活跃营销活动更改任一时间安排字段(start_date、end_date、timezone、days、start_time、end_time、schedule_windows、entry_delay_minutes)时,会立即重新安排下一次发送,因此清除未来的开始日期或缩短加入后的等待时间会立即生效。主要字段:name、description、status、stop_on_reply、open_tracking、link_tracking、text_only、daily_limit、unsubscribe_header、risky_emails、cc、bcc、start_date、end_date、timezone、days、start_time、end_time、schedule_windows、email_tags、folders、contact_order_by、contact_order_dir、contact_order_field、sender_strategy、rotation_mode、ramp_enabled、ramp_start、ramp_increment、ramp_ceiling、esp_match_mode、max_new_leads_per_day、prioritize_new_leads、entry_delay_minutes、continuous、tracking_domain、utm_tracking、utm_source、utm_medium、utm_campaign。
entry_delay_minutes 以每位联系人加入营销活动的时间为起点,而非活动开始时间,因此下周通过关联分群加入的联系人,也会从自己加入时起等待相同的时长。在此字段引入之前就已加入活动的潜在客户,以活动的 created_at 为起点,因此启用延迟不会让已加入数周的潜在客户重新等待。
设置了 continuous 的营销活动在潜在客户全部处理完毕后保持 active,并在等待期间携带 idle_since;有内容可发送时该时间戳便会清除。持续运行的营销活动可以在没有任何潜在客户时启动;非持续运行且从未有过潜在客户的活动启动时返回 400 no_leads。若活动至少有一位潜在客户,但所有人都已完成邮件序列而无内容可发,启动时会启用 continuous 并等待新潜在客户(参见 启动营销活动)。通过任何途径(此 API、关联分群或自动化流程)向 completed 营销活动添加潜在客户,都会经过与手动启动相同的启动检查来重新启动活动;重启遭拒会记录到活动日志中。
{
"name": "Q3 outbound (renamed)",
"daily_limit": 35,
"stop_on_reply": true,
"schedule_windows": [[],[{"start":540,"end":1020}],[{"start":540,"end":1020}],[],[],[],[]]
}响应
更新后的 Campaign 对象。
删除营销活动
DELETE /campaigns/:id
永久删除营销活动。拥有相应权限的任何工作区成员都可以删除其中任一营销活动,不限于自己创建的活动。权限范围 WRITE_CAMPAIGNS · 组织权限 manage_campaigns。
运行中的营销活动无需先暂停:删除活动的同一事务会取消其待处理任务(等待中的唤醒任务,以及尚未交给工作节点的发送任务),因此不会继续为其发送。已经交给工作节点的发送会完成,但其结果会被丢弃。
随营销活动删除的内容包括:步骤、潜在客户及其进度、活动日志、发件邮箱、A/B 变体、高级设置、每日计数器、发送前检查报告和附件文件。保留的内容包括:联系人、已发送的邮件(仍显示在收件箱及回复会话中)、禁止发送记录、商机和预约(清除其营销活动关联),以及跟踪链接,因此收件人点击先前邮件中的链接时仍可正常跳转。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
响应
204 No Content,响应体为空。
复制营销活动
POST /campaigns/:id/duplicate
根据现有营销活动的配置创建新的草稿营销活动。权限范围 WRITE_CAMPAIGNS · 组织权限 manage_campaigns。与 创建 共用同一每日新建营销活动限流额度。
复制的内容包括:名称(除非另行提供,否则追加 (copy))、描述、所有发送、跟踪、时间安排、轮换、发送量递增、ESP 匹配及自动暂停设置,以及各步骤的主题、正文、等待时间、画布位置和分支图(重新连接到新步骤 ID)、邮箱标签、文件夹、显式发件邮箱列表、A/B 变体、高级设置和附件。
不复制的内容包括:潜在客户、进度、发送/打开/点击/回复统计、活动日志、每日计数器、任务、发送量递增级别、自动暂停触发状态,以及任何已经过去的开始或结束日期(过去的结束日期会让副本一启动就结束)。发件邮箱轮换游标从零开始。副本归调用者所有,初始状态为 draft;在启动之前不会发送任何邮件。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 要复制的营销活动。 |
请求体
可选。
| 字段 | 类型 | 说明 |
|---|---|---|
name | string | 副本名称,3 至 50 个字符。默认为原名称后追加 (copy)。 |
{ "name": "Q3 outbound, subject B" }响应
201 Created,包含新的 Campaign 对象(参见 列表 的结构),其中含有 senders。
获取高级设置
GET /campaigns/:id/advanced
返回营销活动的高级营销设置覆盖(退信处理流程、任务可靠性、A/B 测试、回复意图、发送时间优化、发送前检查、控制台)。权限范围 READ_CAMPAIGNS · 组织权限 view_campaigns。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
响应
一个 CampaignAdvancedSettings 对象:包含营销活动 ID、overrides 配置块和 updated_at。
{
"campaign_id": "8f1d6b2e-2b7a-4c9e-9a1f-0e6d4c3b2a10",
"overrides": {
"bounce_pipeline": {
"enabled": true,
"auto_suppress_on_bounce": true,
"auto_suppress_on_complaint": true,
"auto_suppress_on_unsubscribe": true,
"auto_pause_campaign_on_spike": true,
"pause_bounce_rate_threshold": 8,
"pause_complaint_rate_threshold": 1.5
},
"task_reliability": { "enabled": true, "dlq_enabled": true, "max_attempts": 5, "execution_window_seconds": 300 },
"ab_testing": { "enabled": true, "default_winning_rule": "reply_rate", "auto_promote_winner": false, "min_sample_size": 30 },
"reply_intent": {
"enabled": true,
"positive_keywords": ["interested", "pricing"],
"negative_keywords": ["not interested", "unsubscribe"],
"out_of_office_keywords": ["out of office", "vacation"],
"question_keywords": ["?", "how", "price"],
"auto_create_crm_task": true,
"crm_task_intents": ["positive", "question", "neutral", "negative"],
"auto_pause_on_negative": false,
"auto_suppress_on_unsubscribe_keyword": true,
"hold_on_out_of_office": true,
"out_of_office_hold_days": 7
},
"send_time_optimization": {
"enabled": false,
"use_contact_timezone": true,
"default_contact_timezone": "UTC",
"preferred_hours": [9, 10, 11, 14, 15, 16],
"weekend_weight_multiplier": 0.5
},
"preflight": {
"enabled": true,
"check_tracking_domain": true,
"check_unsubscribe_header": true,
"check_ab_variant_configured": false,
"check_daily_limit": true,
"check_schedule_window": true,
"check_content_score": true,
"min_content_score": 60
},
"dashboard": { "enabled": true, "show_suppression_log": true, "show_intent_summary": true, "show_dlq_stats": true }
},
"updated_at": "2026-06-10T12:00:00Z"
}更新高级设置
PATCH /campaigns/:id/advanced
替换营销活动的高级设置覆盖。权限范围 WRITE_CAMPAIGNS · 组织权限 manage_settings。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
settings | object | 是 | 完整的 AdvancedOutreachSettings 配置块(结构与上方的 overrides 相同)。 |
{
"settings": {
"bounce_pipeline": { "enabled": true, "auto_suppress_on_bounce": true, "auto_suppress_on_complaint": true, "auto_suppress_on_unsubscribe": true, "auto_pause_campaign_on_spike": true, "pause_bounce_rate_threshold": 8, "pause_complaint_rate_threshold": 1.5 },
"task_reliability": { "enabled": true, "dlq_enabled": true, "max_attempts": 5, "execution_window_seconds": 300 },
"ab_testing": { "enabled": true, "default_winning_rule": "reply_rate", "auto_promote_winner": false, "min_sample_size": 30 },
"reply_intent": { "enabled": true, "positive_keywords": [], "negative_keywords": [], "out_of_office_keywords": [], "question_keywords": [], "auto_create_crm_task": true, "crm_task_intents": ["positive", "question", "neutral", "negative"], "auto_pause_on_negative": false, "auto_suppress_on_unsubscribe_keyword": true, "hold_on_out_of_office": true, "out_of_office_hold_days": 7 },
"send_time_optimization": { "enabled": true, "use_contact_timezone": true, "default_contact_timezone": "UTC", "preferred_hours": [9, 14], "weekend_weight_multiplier": 0.5 },
"preflight": { "enabled": true, "check_tracking_domain": true, "check_unsubscribe_header": true, "check_ab_variant_configured": false, "check_daily_limit": true, "check_schedule_window": true, "check_content_score": true, "min_content_score": 60 },
"dashboard": { "enabled": true, "show_suppression_log": true, "show_intent_summary": true, "show_dlq_stats": true }
}
}响应
204 No Content,响应体为空。
列出 A/B 测试版本
GET /campaigns/:id/ab-variants
列出营销活动的 A/B 变体。指定 step_id 的变体仅作用于一个步骤;序列 ID 为 null 时,变体作用于整个营销活动。权限范围 READ_CAMPAIGNS · 组织权限 view_campaigns。
原始版本(Original)也是按权重分配流量的实验组
在步骤级测试中,步骤本身的邮件是原版(对照组)。默认情况下它占有均等份额;也可以在该 step_id 上创建一个 is_control: true 变体,以其 weight 显式指定原版的份额。该行的 subject/body 会被忽略;选中对照组时,发送步骤自身的内容。A/B 分析的变体列表不包含对照组行。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
响应
包含 CampaignABVariant 对象的 data 数组(无分页封装)。
{
"data": [
{
"id": "c1a2...",
"campaign_id": "8f1d6b2e-2b7a-4c9e-9a1f-0e6d4c3b2a10",
"step_id": "7e3b...",
"name": "Subject B",
"weight": 50,
"subject": "Worth a look, {{first_name}}?",
"body_html": "<p>Hi {{first_name}}...</p>",
"body_plain": "Hi {{first_name}}...",
"is_control": false,
"is_active": true,
"created_at": "2026-06-02T10:00:00Z",
"updated_at": "2026-06-02T10:00:00Z"
}
]
}创建 A/B 测试版本
POST /campaigns/:id/ab-variants
向营销活动添加变体(或通过 step_id 向单个步骤添加)。权限范围 WRITE_CAMPAIGNS · 组织权限 manage_settings。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
name | string | 是 | 变体标签。 |
step_id | uuid | 否 | 变体适用的步骤,省略则为营销活动级。必须是此营销活动中的步骤,否则请求返回 400。 |
weight | integer | 否 | 相对选取权重(1–100)。各份额由参与测试的各组权重归一化后得到,因此两组权重相同时平分流量。 |
subject | string | 否 | 变体主题模板。为空时复用步骤的主题。步骤在联系人的现有会话中回复(thread_reply)时,此字段会被忽略并沿用会话主题。若步骤开启新会话,在该会话中继续回复的跟进邮件会沿用该联系人实际收到的测试组主题。 |
body_html | string | 否 | 变体的 HTML 正文。 |
body_plain | string | 否 | 变体的纯文本正文。 |
is_control | boolean | 否 | 将此变体标记为步骤的对照组。对于步骤级测试,创建一行 is_control 可设置原版份额;其 weight 为原版份额,内容则被忽略(选中对照组时发送步骤自身的邮件)。 |
is_active | boolean | 否 | 变体是否参与流量分配。 |
metadata | object | 否 | 自由格式的元数据。 |
{
"name": "Subject B",
"step_id": "7e3b...",
"weight": 50,
"subject": "Worth a look, {{first_name}}?",
"body_plain": "Hi {{first_name}}...",
"is_control": false,
"is_active": true
}响应
201 Created,包含创建的 CampaignABVariant 对象(参见 变体列表 的结构)。
更新 A/B 测试版本
PATCH /campaigns/:id/ab-variants/:variantId
更新变体。省略的字段保持不变。权限范围 WRITE_CAMPAIGNS · 组织权限 manage_settings。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
variantId | path | uuid | 变体 ID。 |
请求体
所有字段均可选:name、weight、subject、body_html、body_plain、is_control、is_active、metadata。
{ "weight": 70, "is_active": true }响应
更新后的 CampaignABVariant 对象。
删除 A/B 测试版本
DELETE /campaigns/:id/ab-variants/:variantId
删除变体。权限范围 WRITE_CAMPAIGNS · 组织权限 manage_settings。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
variantId | path | uuid | 变体 ID。 |
响应
204 No Content,响应体为空。
获取 A/B 测试分析
GET /campaigns/:id/ab-analysis
返回各变体的互动统计,以及为营销活动计算出的胜出变体。权限范围 READ_ANALYTICS · 组织权限 view_analytics。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
响应
一个 ABWinnerAnalysis 对象。
{
"campaign_id": "8f1d6b2e-2b7a-4c9e-9a1f-0e6d4c3b2a10",
"variants": [
{
"variant_id": "c1a2...",
"variant_name": "Subject A",
"total_sent": 120,
"opened": 78,
"clicked": 21,
"replied": 9,
"bounced": 2,
"open_rate": 65.0,
"click_rate": 17.5,
"reply_rate": 7.5,
"bounce_rate": 1.7
}
],
"winner_id": "c1a2...",
"winner_name": "Subject A",
"winning_rule": "reply_rate",
"confidence": "low"
}列出附件
GET /campaigns/:id/attachments
列出营销活动的所有附件。每项均包含短期有效的预签名下载 url 及其 step_id:限定在某个步骤的文件仅由该步骤发送;step_id 为 null 时,营销活动的每个步骤都会发送该文件。权限范围 READ_CAMPAIGNS · 组织权限 view_campaigns。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
响应
包含附件对象的 data 数组(无分页封装)。
{
"data": [
{
"id": "a9f0...",
"campaign_id": "8f1d6b2e-2b7a-4c9e-9a1f-0e6d4c3b2a10",
"step_id": null,
"filename": "one-pager.pdf",
"size": 248192,
"mime_type": "application/pdf",
"url": "https://storage.warmbly.com/...signed...",
"created_at": "2026-06-05T08:00:00Z"
}
]
}上传附件
POST /campaigns/:id/attachments
上传文件,作为营销活动或其中某个步骤的附件。使用 multipart/form-data 发送,而非 JSON。权限范围 WRITE_CAMPAIGNS · 组织权限 manage_campaigns。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
file | form (multipart) | file | 必填。要上传的文件(最大 15 MB)。不接受可执行文件和脚本类型。 |
step_id | form (multipart) | uuid | 可选。将附件限定到此营销活动的某个邮件序列步骤,此后只有该步骤发送该附件。省略时,每个步骤都会附加该文件。指定其他营销活动的步骤时返回 404。 |
响应
201 Created,包含创建的附件对象(结构与 附件列表 的单个元素相同)。
删除附件
DELETE /campaigns/:id/attachments/:attachmentId
删除营销活动附件及其存储对象。权限范围 WRITE_CAMPAIGNS · 组织权限 manage_campaigns。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
attachmentId | path | uuid | 附件 ID。 |
响应
204 No Content,响应体为空。
执行发送前检查
POST /campaigns/:id/preflight
运行营销活动的发送前验证检查(跟踪域名、退订邮件头、每日上限、发送时段、A/B 配置等),并返回带评分的报告。不发送邮件。权限范围 SEND_CAMPAIGNS · 组织权限 send_campaigns。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
响应
一个 PreflightReport 对象。
{
"id": "f2b1...",
"organization_id": "11111111-2222-3333-4444-555555555555",
"campaign_id": "8f1d6b2e-2b7a-4c9e-9a1f-0e6d4c3b2a10",
"passed": false,
"score": 80,
"checks": [
{
"key": "tracking_domain",
"passed": false,
"severity": "warning",
"message": "2 sender account(s) have an unverified tracking domain.",
"remediation": "Set a tracking domain on every sender account used by this campaign and verify its CNAME."
}
],
"recommendations": ["Verify your tracking domain to improve link attribution."],
"created_at": "2026-06-10T12:00:00Z"
}发送测试邮件
POST /campaigns/:id/test-email
通过指定邮箱,将某个邮件序列步骤的一次性测试邮件发送给指定收件人。邮件的组装方式与真实发送相同:根据联系人解析合并字段和随机文本语法,附加该步骤应发送的文件(营销活动中未限定步骤的附件,加上该步骤自己的附件),追加邮箱签名和营销活动的退订页脚;纯文本营销活动不包含 HTML 部分。主题前添加 [TEST]。测试邮件的打开和点击不会被跟踪,其退订链接不指向任何联系人,因此点击链接不会禁止向任何人发送。省略 step_id 时,默认使用第一步。权限范围 SEND_CAMPAIGNS · 组织权限 send_campaigns。
可使用组织中的任意邮箱,不限于调用者连接的邮箱,也不要求位于营销活动的发件邮箱池中。带有 allowed_email_accounts 列表的 API 密钥只能通过列表内的邮箱进行测试。提供 contact_id 还要求 READ_CONTACTS(组织权限 view_contacts),因为渲染邮件内容需要读取该联系人的字段。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
account_id | uuid | 是 | 发件邮箱 ID。 |
recipient | string (email) | 是 | 测试邮件的收件地址。 |
step_id | uuid | 否 | 要渲染并发送的步骤,默认为第一步。 |
contact_id | uuid | 否 | 用于渲染邮件内容的组织联系人,包含其自定义字段。省略时使用占位联系人进行渲染(Test Company 的 Test Recipient,使用收件人地址,无自定义字段)。 |
{
"account_id": "5c7d...",
"recipient": "[email protected]",
"step_id": "7e3b...",
"contact_id": "9a2c..."
}响应
仅在提供了 contact_id 时,响应中才会包含此字段。
{
"message": "test email sent",
"recipient": "[email protected]",
"subject": "Quick question, {{.FirstName}}",
"account_id": "5c7d...",
"step_id": "7e3b...",
"contact_id": "9a2c..."
}错误
| 状态 | 错误码 | 发生条件 |
|---|---|---|
404 | not_found | 营销活动、步骤、邮箱或联系人不属于调用者的组织。 |
403 | forbidden | 提供了 contact_id,但没有联系人读取权限;或 account_id 不在 API 密钥允许使用的邮箱中。 |
400 | bad_request | 营销活动没有步骤。 |
启动营销活动
POST /campaigns/:id/start
启动(激活)营销活动,开始实际发送邮件。可从 draft、任意暂停状态或 completed 启动(因结束日期已过而关闭的活动,可在延长或清除该日期后恢复)。无内容可发送的活动不会再次结束:若 continuous 尚未启用,此次启动会将其开启,活动进入 active 并设置 idle_since 以等待潜在客户;此切换会写入活动日志。权限范围 SEND_CAMPAIGNS · 组织权限 send_campaigns。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
acknowledge_list_risk | body | boolean | 可选。即使名单的预估退信率原本会导致拒绝(list_bounce_risk),仍然启动;适用于已通过其他渠道验证的名单。 |
若剩余潜在客户全部未通过地址验证而导致启动遭拒,返回 leads_undeliverable,并使营销活动停留在 paused_undeliverable。重新验证潜在客户,或通过 POST /contacts/verification 将其标记为可投递,即可恢复活动。
若营销活动的某个邮件步骤在两种正文中均无内容,则以 empty_step_body 拒绝启动,以免向触达的每位潜在客户发送空白邮件。
响应
{ "status": "started", "waiting_for_leads": false }若启动时无内容可发送,且营销活动现已处于 active 并设置了 idle_since,则 waiting_for_leads 为 true。
停止营销活动
POST /campaigns/:id/stop
停止(暂停)活跃的营销活动。权限范围 SEND_CAMPAIGNS · 组织权限 send_campaigns。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
响应
{ "status": "stopped" }获取潜在客户的暂停状态
GET /campaigns/:id/leads/:contact_id/hold
读取某个联系人在此营销活动中的流程当前是否暂停。暂停会让该潜在客户的下一步等待,不会让联系人退订,也不会将其移出活动;外出自动回复和成员手动暂停潜在客户这两种情况会写入暂停记录。权限范围 READ_CAMPAIGNS · 组织权限 view_campaigns。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
contact_id | path | uuid | 联系人 ID。必须已是此营销活动的潜在客户。 |
响应
{
"campaign_id": "8f1d6b2e-2b7a-4c9e-9a1f-0e6d4c3b2a10",
"contact_id": "3a5e9c71-4f2b-4d88-9a0c-1b7e5d2f6c34",
"hold": {
"since": "2026-09-07T09:14:00Z",
"until": "2026-09-09T00:00:00Z",
"reason": "back 8 Sep 2026",
"source": "out_of_office"
}
}潜在客户未暂停时不包含 hold,包括已到期的定时暂停。暂停没有结束时间时不包含 until,此时只能通过恢复操作解除。source 为 out_of_office、inbox_tagging、manual,或在联系人于此营销活动中 被抄送另一位潜在客户的邮件 时为 cc;cc 暂停的 reason 是另一位潜在客户的地址。
错误
| 状态 | 错误码 | 发生条件 |
|---|---|---|
404 | not_found | 营销活动不属于调用者的组织,或联系人不是该活动的潜在客户。 |
暂停向潜在客户发送
POST /campaigns/:id/leads/:contact_id/pause
暂停某个联系人在单个营销活动中的流程。联系人保持订阅状态,仍然是潜在客户;解除暂停时,邮件序列从停止的位置继续。相较于保持潜在客户原状,以及让联系人退订或将地址加入禁止发送名单这两项永久操作,这提供了针对单个联系人的临时控制。权限范围 WRITE_CAMPAIGNS · 组织权限 manage_campaigns。
请求声明完整的暂停状态,而非增量更改;替换仍有效的暂停时会保留原始开始时间,因此重试会作用于完全相同的记录,无需 Idempotency-Key。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
contact_id | path | uuid | 联系人 ID。必须已是此营销活动的潜在客户。 |
until | body | string | 解除暂停的 RFC 3339 时间戳。必须是未来且在一年以内。省略或发送 null 时表示无限期暂停,只能通过恢复操作解除。 |
reason | body | string | 可选备注,在控制台中显示于暂停状态旁。会去除首尾空白,最多 200 个字符。 |
手动暂停始终优先:它会替换外出自动回复写入的暂停,后续自动回复既不会缩短它,也不会将其覆盖。
请求体
{ "until": "2026-09-21T17:00:00Z", "reason": "On holiday, asked to follow up later" }响应
结构与 获取潜在客户的暂停状态 相同。
错误
| 状态 | 错误码 | 发生条件 |
|---|---|---|
400 | bad_request | until 已过去,或距离现在超过一年。 |
404 | not_found | 营销活动不属于调用者的组织,或联系人不是该活动的潜在客户。 |
恢复向潜在客户发送
POST /campaigns/:id/leads/:contact_id/resume
立即解除暂停。暂停持续的时间会被丢弃,而不会累加到原计划中,因此步骤恢复到未暂停时本应采用的时间安排:若该时间已过,则在营销活动下一次处理时执行;否则在步骤自身的等待时间结束时执行。营销活动自身的唤醒时间也会相应提前;若活动在潜在客户暂停期间已经结束,则通过常规启动检查重新启动。权限范围 WRITE_CAMPAIGNS · 组织权限 manage_campaigns。
恢复未暂停的潜在客户也会成功,且不作任何更改,因此可以安全重试。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
contact_id | path | uuid | 联系人 ID。 |
响应
{
"campaign_id": "8f1d6b2e-2b7a-4c9e-9a1f-0e6d4c3b2a10",
"contact_id": "3a5e9c71-4f2b-4d88-9a0c-1b7e5d2f6c34"
}错误
| 状态 | 错误码 | 发生条件 |
|---|---|---|
404 | not_found | 营销活动不属于调用者的组织,或联系人不是该活动的潜在客户。 |
获取潜在客户的抄送设置
GET /campaigns/:id/leads/:contact_id/cc
列出营销活动向某位潜在客户发送的每封邮件中都会抄送的联系人。参见 为某位潜在客户抄送同事。权限范围 READ_CAMPAIGNS 和 READ_CONTACTS · 组织权限 view_campaigns 和 view_contacts。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
contact_id | path | uuid | 联系人 ID。必须已是此营销活动的潜在客户。 |
响应
{
"campaign_id": "8f1d6b2e-2b7a-4c9e-9a1f-0e6d4c3b2a10",
"contact_id": "3a5e9c71-4f2b-4d88-9a0c-1b7e5d2f6c34",
"cc": [
{
"contact_id": "b61c0e84-2d9f-4a57-8e3b-6f0a1c2d4e59",
"email": "[email protected]",
"first_name": "Jonas",
"last_name": "Weber",
"company": "Acme GmbH",
"status": "active"
}
]
}status 表示下一封邮件是否会抄送给他们:active 会;unsubscribed(已退订或被禁止发送)、bounced(在此会话或发给他们自己的营销活动邮件中发生退信)和 undeliverable(未通过营销活动规则要求的验证)则会被排除,直到状态改变。若此潜在客户会话中的退信被归因到该抄送联系人,则设置 bounced_at。列表保留设置时的顺序。
错误
| 状态 | 错误码 | 发生条件 |
|---|---|---|
404 | not_found | 营销活动不属于调用者的组织,或联系人不是该活动的潜在客户。 |
设置潜在客户的抄送对象
PUT /campaigns/:id/leads/:contact_id/cc
替换营销活动向某位潜在客户发送的每封邮件中都会抄送的联系人,包括跟进邮件。空列表将移除所有抄送联系人。权限范围 WRITE_CAMPAIGNS 和 READ_CONTACTS · 组织权限 manage_campaigns 和 view_contacts。
请求体包含完整列表,因此重试会得到相同状态,无需 Idempotency-Key。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
contact_id | path | uuid | 联系人 ID。必须已是此营销活动的潜在客户。 |
contact_ids | body | array of uuid | 要抄送的工作区联系人,最多 2 位。忽略重复项。 |
若抄送联系人本身也是此营销活动的潜在客户,只要仍有任何潜在客户将其列为抄送对象,其自己的邮件序列就会以 source 为 cc 的方式暂停,因此不会从同一营销活动收到两个会话的邮件。移除抄送会解除暂停。
请求体
{ "contact_ids": ["b61c0e84-2d9f-4a57-8e3b-6f0a1c2d4e59"] }响应
结构与 获取潜在客户的抄送联系人 相同。
错误
| 状态 | 错误码 | 发生条件 |
|---|---|---|
400 | bad_request | contact_ids 中的某项不是 uuid。 |
400 | lead_cc_limit | 联系人超过 2 位。 |
400 | lead_cc_self | 潜在客户被列入了自己的抄送列表。 |
404 | not_found | 营销活动不属于调用者的组织,或联系人不是该活动的潜在客户。 |
404 | lead_cc_contact_not_found | 要抄送的联系人不属于此工作区。 |
409 | lead_cc_lead_is_copied | 该潜在客户已是此营销活动中另一位潜在客户的抄送对象,因此不会收到自己独立的邮件,也就无法再为其抄送他人。 |
409 | lead_cc_has_copies | 要抄送的联系人在此营销活动中已有自己的抄送对象。 |
推荐可抄送的同事
GET /campaigns/:id/leads/:contact_id/cc/suggestions
返回最多八位可能是该潜在客户同事的联系人:公司名称相同,或邮箱域名相同且该域名属于公司而非个人邮件服务。排除已退订和已经被抄送的联系人。权限范围 READ_CAMPAIGNS 和 READ_CONTACTS · 组织权限 view_campaigns 和 view_contacts。
响应
{
"data": [
{
"contact_id": "b61c0e84-2d9f-4a57-8e3b-6f0a1c2d4e59",
"email": "[email protected]",
"first_name": "Jonas",
"last_name": "Weber",
"company": "Acme GmbH",
"reason": "company"
}
]
}公司名称匹配时,reason 为 company;仅邮箱域名匹配时,为 domain。公司匹配项排在前面。
获取营销活动日志
GET /campaigns/:id/logs
分页浏览营销活动日志(状态变更、发送事件、错误)。权限范围 READ_CAMPAIGNS · 组织权限 view_campaigns。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
cursor | query | string | 上一页的不透明游标。可选。 |
limit | query | integer | 每页条数,1 至 100(默认为 50)。可选。 |
响应
包含 data 和 pagination 的响应结构。这里的 pagination 仅包含 next_cursor(字符串,最后一页为 null)和 has_more。
{
"data": [
{
"id": "9b2c...",
"campaign_id": "8f1d6b2e-2b7a-4c9e-9a1f-0e6d4c3b2a10",
"event_type": "campaign_started",
"message": "Campaign started",
"metadata": {},
"created_at": "2026-06-10T12:00:00Z"
}
],
"pagination": {
"next_cursor": "9b2c...",
"has_more": true
}
}今日发送计划
GET /campaigns/:id/send-plan
通过调度器自身的各项限制即时计算营销活动今天的发送量,以及决定该发送量的每项限制。不存储或写入任何内容。权限范围 READ_CAMPAIGNS · 组织权限 view_campaigns。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
响应
计算关系如下:configured_ceiling 减去每项 limits[].emails 再减去 sent_today,得到 expected_remaining;projected_today 等于 sent_today 加 expected_remaining。limits 仅列出实际减少了发送量的限制,并按调度器应用的顺序排列;bottleneck 标明最终决定发送量的限制(没有限制生效时为 "",所有邮箱均已用完当日额度时为 budget_spent)。day 是按 UTC 计算的额度日期(无论营销活动使用何种时区,所有每日计数器均在 UTC 午夜重置);发送时段的时间则使用营销活动时区。leads.waiting_on_sender 统计其专属邮箱今天已无剩余额度的到期步骤,因为每位联系人始终使用最初联系他们的发件地址。organization 仅在工作区套餐有每日额度时出现。
{
"campaign_id": "8f1d6b2e-2b7a-4c9e-9a1f-0e6d4c3b2a10",
"status": "active",
"day": "2026-09-16",
"timezone": "Europe/Paris",
"computed_at": "2026-09-16T12:30:00Z",
"configured_ceiling": 150,
"projected_today": 20,
"sent_today": 8,
"expected_remaining": 12,
"bottleneck": "warmup_graduation",
"limits": [
{ "kind": "warmup_graduation", "emails": 130, "mailboxes": 3 }
],
"window": {
"sending_day": true,
"open_now": true,
"closes_at": "2026-09-16T15:00:00Z",
"minutes_left": 150
},
"leads": {
"due_now": 340,
"due_later_today": 12,
"new_leads_due_today": 300,
"waiting_on_step": 200,
"waiting_on_condition": 0,
"held": 3,
"waiting_on_sender": 0,
"new_leads_started_today": 8,
"max_new_leads_per_day": 0
},
"mailboxes": [
{
"id": "1c2d...",
"email": "[email protected]",
"provider": "gmail",
"configured_cap": 50,
"cap_today": 10,
"limited_by": "warmup_graduation",
"sent_today": 4,
"sent_by_other_campaigns": 0,
"expected_remaining": 6,
"state": "sending",
"min_gap_seconds": 600,
"graduation": { "ceiling": 10, "mailbox_cap": 50, "days_to_full_cap": 8, "held": false }
}
],
"next_wake_at": "2026-09-16T12:41:00Z"
}limits[].kind 为以下值之一:campaign_daily_limit、campaign_ramp、warmup_graduation、workspace_risk、domain_auth、resting、warmup_health_hold、cloud_status_hold、send_recovery、send_cooldown、other_campaigns、warmup_health_pace、mailbox_hours、sending_behavior、spacing、sending_window、not_running、org_daily_limit、new_lead_cap 或 leads。mailboxes[].state 为以下值之一:sending、budget_spent、hours_closed、no_working_day、domain_auth、resting、health_hold、cloud_status_hold(Warmbly Cloud 尚未报告当前状态,因此邮箱的冷邮件发送会等待状态报告;这不是 Cloud 隔离或封禁)、send_recovery、send_cooldown、send_authority、no_worker(目前没有运行中的发送工作节点持有此邮箱;其当日额度仍会计入,因为几分钟内就会重新分配给工作节点)或 window_closed;mailboxes[].limited_by 标明决定 cap_today 的限制。各项限制的含义参见 营销活动指南。
列出营销活动发件人
GET /campaigns/:id/senders
返回营销活动的显式发件邮箱池(sender_strategy 为 explicit 时使用)。权限范围 READ_CAMPAIGNS · 组织权限 view_campaigns。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
响应
包含发件邮箱对象的 data 数组(无分页封装)。
{
"data": [
{
"email_account_id": "5c7d...",
"weight": 1,
"last_sent_at": "2026-06-10T11:55:00Z",
"enabled": true
}
]
}替换发件人列表
PUT /campaigns/:id/senders
以提供的列表原子替换营销活动的显式发件邮箱池。权限范围 WRITE_CAMPAIGNS · 组织权限 manage_campaigns。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
senders | object[] | 是 | 完整的新发件邮箱池。每项包含:email_account_id(uuid,必填)、weight(integer,可选)、enabled(boolean,可选)。空数组会清空邮箱池。其影响取决于营销活动的 sender_strategy:tags 活动回退到标签匹配的邮箱或所有活跃邮箱;explicit 活动只回退到标签匹配的邮箱,若没有标签匹配项,则没有可用发件邮箱,活动停留在 paused_no_accounts。 |
{
"senders": [
{ "email_account_id": "5c7d...", "weight": 2, "enabled": true },
{ "email_account_id": "6d8e...", "weight": 1, "enabled": true }
]
}响应
包含最终发件邮箱对象的 data 数组(结构与 发件邮箱列表 相同)。
列出已关联的联系人分群
GET /campaigns/:id/segments
返回关联到营销活动、作为实时受众来源的分群。权限范围 READ_CAMPAIGNS · 组织权限 view_campaigns。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
响应
包含关联对象的 data 数组(无分页封装)。计数在请求时计算:contact_count 是分群当前匹配的联系人数,lead_count 是其中已成为此营销活动潜在客户的人数,held_out_count 是因手动从营销活动移除而未成为潜在客户的人数(参见 替换关联分群)。
{
"data": [
{
"segment_id": "4b9e...",
"name": "Warm leads",
"color": "#0ea5e9",
"description": "Replied or clicked in the last 30 days",
"contact_count": 412,
"lead_count": 409,
"held_out_count": 3,
"linked_at": "2026-06-10T12:00:00Z"
}
]
}替换已关联的联系人分群
PUT /campaigns/:id/segments
以提供的集合原子替换营销活动的关联分群(最多 20 个)。新关联分群的所有现有成员会立即作为潜在客户加入;之后进入关联分群的联系人也会自动加入,通常在约 2 分钟内完成。
从集合中移除分群,会撤回由它加入的潜在客户,因此用一个分群替换另一个后,营销活动保留的是新受众,而非两者的合集。仅在以下三个条件同时满足时才撤回潜在客户:该潜在客户由关联分群加入(通过其他途径加入的潜在客户视为手动选定,永不撤回);该联系人当前仍属于被移除的某个分群,且不属于任何保留的分群;营销活动尚未向其发信(没有已分发或已发送的步骤)。此次撤回不会记录为手动移除,因此重新关联分群时会再次加入这些成员。仅仅离开仍有关联的分群的联系人,会保留其潜在客户记录;关联期间的加入行为始终只增不减。手动从营销活动移除的潜在客户不会自动重新加入;手动添加(或下方的一次性加入操作)会清除移除记录。省略 segment_ids 返回 400;应显式发送空数组以解除所有分群关联。活跃营销活动会被唤醒,向新潜在客户发送;关联分群新增成员时,已完成的营销活动会经过完整启动检查后重新启动。关联中的分群必须先在此处移除,才能删除(否则 DELETE /segments/:id 返回 409)。权限范围 WRITE_CAMPAIGNS · 组织权限 manage_campaigns。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
segment_ids | uuid[] | 是 | 完整的新关联分群集合,最多 20 个。空数组会解除所有关联。 |
{
"segment_ids": ["4b9e...", "7c2f..."]
}响应
返回最终关联列表,以及本次调用对营销活动潜在客户的影响。added 是新增潜在客户数;若所有成员已是潜在客户、分群尚未匹配到联系人,或成员都被排除,则为 0;各关联的计数可用于区分这些情况。withdrawn 是被移除分群撤回的潜在客户数,contacted 是其中因活动已向其发过邮件而保留的人数。关联、撤回和加入在同一个事务中写入,因此失败时返回错误且不作任何更改,而不是返回 200 和 added: 0。
{
"data": [
{
"segment_id": "4b9e...",
"name": "Warm leads",
"color": "#0ea5e9",
"description": "Replied or clicked in the last 30 days",
"contact_count": 412,
"lead_count": 412,
"held_out_count": 0,
"linked_at": "2026-06-10T12:00:00Z"
}
],
"added": 397,
"withdrawn": 58,
"contacted": 4
}如果需要按当前快照一次性加入,而非建立实时关联,参见 将分群添加到营销活动。
验证营销活动跟踪域名
POST /campaigns/:id/tracking-domain/verify
检查营销活动级跟踪域名是否解析到本实例的跟踪主机,成功后将 tracking_domain_verified 设为 true。无法正确解析的记录会保持未验证状态并提供原因,而不是抛出错误;营销活动会回退到邮箱的域名(或共用主机)。权限范围 WRITE_CAMPAIGNS · 组织权限 manage_campaigns。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
响应
一个 TrackingDomainStatus 对象,结构与 邮箱跟踪域名端点 的返回值相同。
{
"tracking_domain": "track.acme.com",
"tracking_domain_verified": true,
"tracking_domain_verified_at": "2026-06-10T12:00:00Z",
"cname_target": "t.warmbly.com",
"status": "verified",
"message": "track.acme.com points at t.warmbly.com.",
"observed": "t.warmbly.com",
"tracking_host_unresolvable": false
}列出邮件序列
GET /campaigns/:id/steps
按顺序返回营销活动的邮件序列步骤。权限范围 READ_CAMPAIGNS · 组织权限 view_campaigns。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
响应
直接返回 Sequence 对象数组(无封装)。
[
{
"id": "7e3b...",
"name": "Step 1",
"subject": "Quick question, {{first_name}}",
"body_plain": "Hi {{first_name}}...",
"body_html": "<p>Hi {{first_name}}...</p>",
"body_sync": true,
"body_code": false,
"wait_after": 0,
"position": 0,
"thread_reply": true,
"kind": "email",
"updated_at": "2026-06-02T10:00:00Z",
"created_at": "2026-06-02T10:00:00Z"
}
]创建邮件序列
POST /campaigns/:id/steps
向营销活动追加新的邮件序列步骤。权限范围 WRITE_CAMPAIGNS · 组织权限 manage_campaigns。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
请求体
可选。接受与 更新邮件序列 相同的字段,并应用于新步骤,因此可以在一次请求中创建步骤及其邮件内容。不提供请求体或提供 {} 时,使用默认值创建步骤。若请求体不符合更新操作的要求,则不会创建任何内容,并返回该更新错误。
{
"subject": "Quick question",
"body_plain": "Hi {{first_name}}, ...",
"wait_after": 3
}响应
创建的 Sequence 对象(参见 邮件序列列表 的结构)。
更新邮件序列
PATCH /campaigns/:id/steps/:sid
更新邮件序列步骤:邮件内容、间隔、节点类型、分支树或操作配置。省略的字段保持不变。权限范围 WRITE_CAMPAIGNS · 组织权限 manage_campaigns。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
sid | path | uuid | 邮件序列(步骤)ID。 |
请求体
所有字段均可选。
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
name | string | 否 | 步骤标签。 |
subject | string | 否 | 主题模板。 |
body_plain | string | 否 | 纯文本正文模板。留空时,发送流程会根据 body_html 生成纯文本,保留列表项目符号、表格行和链接目标,并去除样式表。 |
body_html | string | 否 | HTML 正文模板。按原样发送,包括含有自身 <head> 的完整文档。发送时,任何 <style> 块都会内联到其匹配的元素上;参见 邮件序列指南。若仅发送 body_plain,则会据此生成 HTML 部分,因为只有纯文本正文而 HTML 为空的步骤原本会显示为空白:所有现代客户端都会优先选择 HTML 版本。已具有 HTML 正文的步骤不会被覆盖。 |
body_sync | boolean | 否 | 使纯文本和 HTML 正文保持同步。 |
body_code | boolean | 否 | 正文以原始 HTML 编写。控制台编辑器会将其作为标记源码打开,而不是解析到只能保留其模式所能表示内容的可视化编辑器中。这不会改变实际发送的内容。 |
wait_after | integer | 否 | 此步骤之前等待的天数,从联系人完成上一步时起算(0 至 60)。间隔属于目标步骤,因此邮件步骤没有独立的等待节点。 |
thread_reply | boolean | 否 | 将此步骤作为联系人现有会话中的回复发送,而非新邮件。默认为 true。会话内回复步骤沿用会话主题,因此只有关闭此设置后,才使用其自身的 subject。此设置对联系人的首封邮件无效,因为没有可回复的邮件。 |
conditions | object | 否 | 从此步骤出发的连接({branches: [...]}),按顺序求值;没有 conditions 的分支是普通的“接下来前往此处”连接。路由只沿连接进行:{} 或没有分支的步骤没有出站路径,会结束此联系人的流程。 |
kind | string | 否 | email(默认)、action 或 wait。 |
action | object | 否 | 非邮件节点的类型化配置。type 是类型选择字段(wait、add_tag、remove_tag、add_to_segment、remove_from_segment(这两个分群操作均带有 segment_id)、unsubscribe、notify、create_task、create_deal、move_deal_stage、run_automation、end),其余字段取决于所选类型。 |
{
"name": "Step 2",
"subject": "Following up, {{first_name}}",
"body_plain": "Just bumping this...",
"wait_after": 2,
"conditions": {
"branches": [
{
"branch_id": "b1",
"target_step_id": null,
"conditions": [{ "field": "replied", "operator": "ever", "value": null }]
}
]
}
}响应
更新后的 Sequence 对象。
删除邮件序列
DELETE /campaigns/:id/steps/:sid
删除邮件序列步骤。权限范围 WRITE_CAMPAIGNS · 组织权限 manage_campaigns。
| 参数 | 位置 | 类型 | 说明 |
|---|---|---|---|
id | path | uuid | 营销活动 ID。 |
sid | path | uuid | 邮件序列(步骤)ID。 |
响应
200 OK,响应体为空。
预览模板
POST /campaign-template-preview
完全按照发送流程为联系人渲染主题和正文模板,并报告解析错误以及任何未解析的 {{...}} 占位符。提供 campaign_id 和 account_id 时,预览还会执行其余邮件组装步骤:按发送顺序应用纯文本规则、邮箱签名及营销活动的退订页脚,并在响应中给出发件人及该邮件携带的附件列表。不插入跟踪像素,也不重写链接。无副作用。权限范围 READ_CAMPAIGNS · 组织权限 view_campaigns。提供 contact_id 还要求 READ_CONTACTS(组织权限 view_contacts);带有 allowed_email_accounts 列表的 API 密钥只能在 account_id 中指定这些邮箱。
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
subject | string | 否 | 主题模板。 |
body_html | string | 否 | HTML 正文模板。 |
body_plain | string | 否 | 纯文本正文模板。 |
contact_id | uuid | 否 | 用于渲染的组织联系人,包含其自定义字段。省略时使用内置示例联系人。 |
contact | object | 否 | 覆盖用于渲染的联系人(示例联系人或 contact_id 指定的联系人)的字段:first_name、last_name、email、company、phone,以及字符串到字符串的 custom_fields 映射。 |
campaign_id | uuid | 否 | 组织中的营销活动,应用其退订页脚、纯文本设置和附件。预览中的退订链接不指向任何联系人。 |
step_id | uuid | 否 | 正在预览的步骤,使 attachments 列出该步骤发送的文件。不提供时,仅列出营销活动全局附件。 |
account_id | uuid | 否 | 组织中的邮箱;启用签名同步时追加其签名,并将其作为 from 返回。 |
{
"subject": "Hi {{first_name}} at {{company}}",
"body_html": "<p>Hey {{first_name}}, I saw {{company}} is hiring. {{unknown_token}}</p>",
"contact_id": "9a2c...",
"campaign_id": "8f1d...",
"account_id": "5c7d..."
}响应
一个 TemplatePreview 对象。errors 列出会阻止发送的模板解析错误;unresolved 列出渲染后仍原样保留的占位符。提供了 account_id 时包含 from;提供了 campaign_id 且发送内容包含文件(营销活动全局附件,加上 step_id 自己的附件)时,包含 attachments。这四个字段为空时均省略。
{
"subject": "Hi Sam at Globex",
"body_html": "<p>Hey Sam, I saw Globex is hiring. {{unknown_token}}</p><br><br><p>Best, Ana</p><p style=\"font-size:12px;color:#64748b\">Don't want these emails? <a href=\"https://app.example.com/u/...\">Unsubscribe</a></p>",
"body_plain": "Hey Sam, I saw Globex is hiring. {{unknown_token}}\n\nBest, Ana\n\nDon't want these emails? https://app.example.com/u/...",
"unresolved": ["{{unknown_token}}"],
"from": { "name": "Ana Silva", "email": "[email protected]" },
"attachments": [
{ "id": "3b0e...", "filename": "deck.pdf", "size": 482113, "mime_type": "application/pdf" }
]
}错误
| 状态 | 错误码 | 发生条件 |
|---|---|---|
404 | not_found | contact_id、campaign_id 或 account_id 不属于调用者的组织。 |
403 | forbidden | 提供了 contact_id,但没有联系人读取权限;或 account_id 不在 API 密钥允许使用的邮箱中。 |
使用写作助手生成文案
POST /generation/write
使用 AI 写作助手生成营销内容。仅对付费和免费试用组织开放,每次调用消耗一个 AI 积分(服务商调用失败时退还)。支持 Idempotency-Key,避免重试请求被重复扣费。权限范围 WRITE_CAMPAIGNS · 组织权限 manage_campaigns。
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
prompt | string | 是 | 用于生成内容的指令(最多 8000 个字符)。 |
tone | string | 否 | 期望的语气(例如 friendly、direct)。 |
{ "prompt": "Write a 3-line cold intro to a fintech founder about our deliverability tooling.", "tone": "direct" }响应
{
"text": "Hi {{first_name}},\n\nNoticed {{company}} is scaling outbound...\n\nWorth a quick chat?",
"credits_remaining": 248,
"model": "claude-..."
}组织积分耗尽时,端点返回 402、code: "insufficient_credits" 及标准响应结构。在调用任何服务商之前会先检查余额是否耗尽,因此返回 402 时绝不会消耗一次生成调用。
预览 AI 变量
POST /generation/ai-variable
生成按收件人定制的 AI 变量块所产生的专属片段,用于营销活动编辑器预览。提示词会基于指定联系人(必须属于此组织)或示例联系人渲染,然后采用与发送流程相同的上下文进行生成,其中包括邮件前后文,以使片段自然融入。按实际用量计费(片段使用的模型和 token,以及任何网页查询),服务商调用失败时退还,并支持 Idempotency-Key 以防止重试请求被重复扣费。参见 AI 变量 指南。权限范围 WRITE_CAMPAIGNS · 组织权限 manage_campaigns 和 use_ai。提供 contact_id 还要求 READ_CONTACTS(组织权限 view_contacts),因为提示词会读取该联系人的字段。
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
mode | string | 否 | instant(默认)执行一次快速生成;research 则采用更深入、成本更高的处理流程。 |
prompt | string | 是 | 变量块指令,本身也是基于联系人渲染的模板(最多 8000 个字符)。 |
tone | string | 否 | 期望的语气。 |
web_search | boolean | 否 | 允许针对联系人的公司执行一次有范围限制的网页查询,以丰富上下文。research 隐含启用此功能。 |
contact_id | string | 否 | 用于渲染提示词的组织联系人。省略时使用示例联系人。 |
context_before | string | 否 | 变量块紧前方的邮件文本,以使片段自然融入所在句子。 |
context_after | string | 否 | 变量块紧后方的邮件文本。 |
{ "mode": "instant", "prompt": "One line noting something specific about {{company}}.", "contact_id": "4f6c..." }响应
{
"text": "Saw Acme just shipped its new billing API.",
"credits_remaining": 246,
"credits_charged": 1,
"tokens_used": 180,
"model": "claude-..."
}contact_id 不属于此组织时返回 404。组织积分耗尽时,端点返回 402 和 code: "insufficient_credits";此检查在调用任何服务商之前进行。