高级功能
邮件追踪 (track)
邮件追踪能够帮助用户收集已发出邮件的「打开数据」,「点击数据」,「退订数据」, 以此来评估邮件发送的效果.
你可以通过【发送设置】-【追踪选项】来配置某个「API_USER」的「打开追踪」,「点击追踪」,「订阅追踪」.
配置追踪域名
使用 ICP 备案通过的域名或其子域名作为追踪域名
配置此域名的 CNAME 记录到 track.sendcloud.org
技术原理
假设客户使用的追踪域名是 track.ifaxin.com,客户需要配置
track.ifaxin.com CNAME track.sendcloud.org
打开追踪
SendCloud 会在客户邮件的末端放置1张1像素的透明图片,一般情况 MP 的客户端、网页端会自动下载邮件中的图片
当用户打开这封邮件时,用户的邮件客户端会请求下载这张1像素的图片,此时 SendCloud 的 track 服务会记录这次打开操作
点击追踪
- 假设客户邮件中有一个链接:http://www.ifaxin.com
- SendCloud 会修改此链接的 href 值变成:http://track.ifaxin.com?p=xxx
- 用户点击链接时,链接会通过 CNAME 记录先请求到 track.sendcloud.org 的 track 服务
- 在完成相应的点击记录之后,track 服务会将此请求 301 到客户的原始链接:http://www.ifaxin.com
取消订阅追踪
SendCloud 会在客户邮件的末端放置1个取消订阅的链接,用户点击此链接会进入取消订阅样式的页面,SendCloud 会记录此用户的邮箱加入到相应的取消订阅列表
取消订阅的链接如图:

地址列表
地址列表是一个方便客户发送邮件的功能. 客户编辑/导入地址列表后, 在请求接口时, 不需要再传输邮件地址, 只需在参数中指定地址列表的「别称地址」即可.
地址列表中可以设置「变量的值」, 用于替换「邮件内容」中的「变量」.
客户可以通过【发送相关】-【地址列表】来创建【地址列表】.
注意:
- 只有通过 WEBAPI 的方式发送邮件, 才支持使用地址列表. SMTP 接入方式暂不支持.
- 只有使用 「批量类型」的 API_USER 调用接口, 才支持使用地址列表
- 只有付费用户才支持使用地址列表
标签 (label)
标签能够帮助客户对邮件进行分类, 适用于 A/B 测试等场景.
给2种风格的邮件赋予不同的标签进行测试发送, 通过标签分别查看2种风格邮件的打开/点击数据 就能知道接收者最喜欢哪种邮件了~
注意: 每封邮件只能使用一个标签
收信路由
如果你能收到你的用户的回信, 那真是一件让人激动的事情, 所以, SendCloud 为你准备了这个贴心的功能.
此功能就是把用户回复到某地址的邮件转发给你设置的邮箱, 或是你设置的某个URL. 你可以接收邮件数据, 再做后续处理.
直接发给这个地址的邮件默认不会被转发.

客户可以通过某个域名配置里面的【收信配置】-【添加收信路由】来创建收信路由.
订阅关系
订阅关系的作用是帮助客户创建, 维护和用户的订阅关系, 帮助客户快速搭建自己的邮件订阅系统.
SendCloud 和 ESP 都在推进建立用户和用户订阅关系, 也会大力支持这种存在订阅关系的邮件发送 ( 比如 QQ邮件列表 ).
-
客户在 SendCloud 中配置生成一个「订阅关系」:

-
客户将生成的 JS 代码放置在自己的网站中
-
用户访问客户的网站时, 输入邮箱地址, 订阅某条信息
-
用户的邮箱地址自动加入客户的某个「地址列表」.
-
用户收到确认邮件, 确认订阅操作.
-
这样, 用户就完成了和客户网站的订阅关系.
此功能依赖于「地址列表」, 客户在配置订阅关系时需要将此订阅关系绑定到某个「地址列表」.
取消订阅样式
在开启「订阅追踪」后, 系统会在邮件中默认自动加上 取消订阅 的退订链接, 供收件人退订此类邮件.
下面是系统默认的取消订阅样式:

你可以自行定义取消订阅的样式, 只要在 href 中插入 SendCloud 内部变量 %%user_defined_unsubscribe_link%% 即可. 示例如下:
使用默认的取消订阅样式, 邮件无需特别处理, 内容如下:
<p>亲爱的%name%:</p>
<p style="margin-bottom: 35px">您好! 您本月在爱发信的消费金额为: %money% 元.</p>
邮件内容效果截图如下:

使用自定义的取消订阅样式, 邮件内容如下:
<p>亲爱的%name%:</p>
<p style="margin-bottom: 35px">您好! 您本月在爱发信的消费金额为: %money% 元.</p>
<p><a style="background: #1ABC9C;border:1px solid #13A386;padding:8px 20px;color: #fff;text-decoration:none;border-radius:4px" href="%%user_defined_unsubscribe_link%%">不想再收到此类邮件</a></p>
邮件内容效果截图如下:

WebHook
客户将邮件请求发送给 SendCloud 之后, SendCloud 会把「请求结果」同步返回给客户, 而邮件的「发送结果」和「其他事件结果」是通过 WebHook 异步返回给用户的.
WebHook 机制:
- SendCloud 为客户提供了一些邮件事件, 客户可以选择关注某些事件
- 当某事件发生, 就会触发 SendCloud 向客户设置的 URL 发送数据 ( POST )
- SendCloud推送的数据类型:"Content-Type: application/x-www-form-urlencoded"
- 客户收到数据, 解析出事件和数据, 你需要在 3s 内返回HTTP Status Code 200, 否则, SendCloud 将会重发该条消息.点此查看WEBHOOK解析示例
目前 SendCloud 支持的邮件事件如下:
| 事件 | 触发条件 |
|---|---|
| 请求(request) | 邮件请求成功 |
| 送达(deliver) | 邮件发送成功 |
| 打开(open) | 用户打开邮件 |
| 点击(click) | 用户点击链接 |
| 取消订阅(unsubscribe) | 用户取消订阅邮件 |
| 举报(report_spam) | 用户举报邮件 |
| 无效邮件(invalid) | 邮件未发送成功 |
| 软退信(soft_bounce) | 接收方拒收该邮件 |
| 转信(route) | 转信/收信路由 |
使用方法:
- 客户自行编写 HTTP 服务, 使之能够处理相应的事件, 解析相关数据, 并开放出相应 URL
- 客户在 SendCloud 的
【邮件】- 【发送设置】-【WebHook】中选择关注的事件, 配置接收数据的 URL
注意: 我们会对用户提供的 URL 做检测. 需要此 HTTP 服务能够正确响应 get | post 请求, 并且保证返回的 HTTP 状态码 为 200。
签名验证
为了确保消息的来源身份是 SendCloud, 你可以选择对 POST 数据的来源进行安全认证. ( 不验证, 直接解析 POST 的数据也可以 ).
安全认证的方法如下:
- 通过
【发送设置】-【WebHook】获取APP KEY - 解析出 POST 数据中的
token,timestamp和signature - 使用
APP KEY,token和timestamp生成签名signature, 与 POST 数据中的signature进行校验 ( 签名算法: SHA256)
python 代码示例
import hashlib, hmac
def verify(appkey, token, timestamp, signature):
return signature == hmac.new(
key=appkey,
msg='{}{}'.format(timestamp, token),
digestmod=hashlib.sha256).hexdigest()
Java 代码示例 (依赖 apache codec)
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Hex;
public boolean verify(String appkey, String token, long timestamp,
String signature) throws NoSuchAlgorithmException, InvalidKeyException {
Mac sha256HMAC = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(appkey.getBytes(),"HmacSHA256");
sha256HMAC.init(secretKey);
StringBuffer buf = new StringBuffer();
buf.append(timestamp).append(token);
String signatureCal = new String(Hex.encodeHex(sha256HMAC.doFinal(buf
.toString().getBytes())));
return signatureCal.equals(signature);
}
php 代码示例
function verify($appkey,$token,$timestamp,$signature){
$hash="sha256";
$result=hash_hmac($hash,$timestamp.$token,$appkey);
return strcmp($result,$signature)==0?1:0;
}
重试机制
如果遇到 URL 访问错误或超时, SendCloud 最多会重试 7 次. 每次重试的时间间隔最快为 3min, 10min, 30min, 1h, 6h, 12h, 24h. 即在消息丢失前, 你有足够的时间来修复 URL.
如果超过重试次数,SendCloud 会将该消息保存15天,如果有需要可以联系我们,进行重新推送.
每次事件处理和数据解析, 你需要在 3s 内返回HTTP Code 200, 否则, SendCloud 将会重发该条消息.
事件说明
目前 WebHook 支持的事件类型包括: 请求, 送达, 打开, 点击, 取消订阅, 举报, 无效邮件.
请求事件 ( request )
参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
| event | string | 事件类型:"request" |
| message | string | 消息内容: "successfully request" |
| maillistTaskId | long | 如果使用地址列表发送,将产生任务id |
| mail_list_task_id | long | 同 maillistTaskId |
| messageId | string | messageId |
| apiUser | string | API_USER |
| category | string | 同 apiUser |
| recipientArray | list | 请求的收件人 |
| emailIds | list | emailId 数组 |
| labelId | int | 自定义的标签ID |
| labelName | string | 自定义的标签名称 |
| sendTags | list | 本次发送所使用的tag数组 |
| recipientSize | int | 本次请求的个数 |
| timestamp | long | 时间戳. 事件产生时间 |
| token | string | 随机产生的长度为50的字符串 |
| signature | string | 签名字符串 |
| userHeaders | string | 客户调用发送接口自定义的, 并且以"SC-Custom-"开头的头部信息 |
送达 ( deliver )
参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
| event | string | 事件类型:"deliver" |
| message | string | 消息内容:"Successfully delivered" |
| apiUser | string | API_USER |
| category | string | 同 apiUser |
| maillistTaskId | long | 如果使用地址列表发送,将产生任务id |
| mail_list_task_id | long | 同 maillistTaskId |
| emailId | string | 每封email的唯一ID |
| recipient | string | 收信人 |
| labelId | int | 自定义的标签ID |
| labelName | string | 自定义的标签名称 |
| sendTags | list | 本次发送所使用的tag数组 |
| timestamp | long | 时间戳 |
| token | string | 随机产生的长度为50的字符串 |
| signature | string | 签名字符串 |
| userHeaders | string | 客户调用发送接口自定义的, 并且以"SC-Custom-"开头的头部信息 |
打开 ( open )
参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
| event | string | 事件类型:"open" |
| message | string | 消息内容:"open email" |
| apiUser | string | API_USER |
| category | string | 同 apiUser |
| maillistTaskId | long | 如果使用地址列表发送,将产生任务id |
| mail_list_task_id | long | 同 maillistTaskId |
| emailId | string | 每封email的唯一ID |
| recipient | string | 收信人 |
| labelId | int | 自定义的标签ID |
| labelName | string | 自定义的标签名称 |
| sendTags | list | 本次发送所使用的tag数组 |
| ip | string | 打开的Ip地址 |
| explorerName | string | 浏览器名称 |
| explorerVer | string | 浏览器版本 |
| oSName | string | 操作系统名称 |
| oSVer | string | 操作系统版本 |
| timestamp | long | 时间戳 |
| token | string | 随机产生的长度为50的字符串 |
| signature | string | 签名字符串 |
| userHeaders | string | 客户调用发送接口自定义的, 并且以"SC-Custom-"开头的头部信息 |
点击 ( click )
参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
| event | string | 事件类型:"click" |
| message | string | 消息内容:"click email" |
| apiUser | string | API_USER |
| category | string | 同 apiUser |
| maillistTaskId | long | 如果使用地址列表发送,将产生任务id |
| mail_list_task_id | long | 同 maillistTaskId |
| emailId | string | 每封email的唯一ID |
| recipient | string | 收信人 |
| labelId | int | 自定义的标签ID |
| labelName | string | 自定义的标签名称 |
| sendTags | list | 本次发送所使用的tag数组 |
| url | string | 被点击的链接 |
| ip | string | 点击的Ip地址 |
| explorerName | string | 浏览器名称 |
| explorerVer | string | 浏览器版本 |
| oSName | string | 操作系统名称 |
| oSVer | string | 操作系统版本 |
| timestamp | long | 时间戳 |
| token | string | 随机产生的长度为50的字符串 |
| signature | string | 签名字符串 |
| userHeaders | string | 客户调用发送接口自定义的, 并且以"SC-Custom-"开头的头部信息 |
取消订阅 ( unsubscribe )
参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
| event | string | 事件类型:"unsubscribe" |
| message | string | 消息内容:"unsubscribe email" |
| apiUser | string | API_USER |
| category | string | 同 apiUser |
| labelId | int | 自定义的标签ID |
| labelName | string | 自定义的标签名称 |
| sendTags | list | 本次发送所使用的tag数组 |
| maillistTaskId | long | 如果使用地址列表发送,将产生任务id |
| mail_list_task_id | long | 同 maillistTaskId |
| emailId | string | 每封email的唯一ID |
| recipient | string | 收信人 |
| ip | string | Ip地址 |
| explorerName | string | 浏览器名称 |
| explorerVer | string | 浏览器版本 |
| oSName | string | 操作系统名称 |
| oSVer | string | 操作系统版本 |
| timestamp | long | 时间戳 |
| token | string | 随机产生的长度为50的字符串 |
| signature | string | 签名字符串 |
| userHeaders | string | 客户调用发送接口自定义的, 并且以"SC-Custom-"开头的头部信息 |
垃圾举报 ( report_spam )
参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
| event | string | 事件类型:"report_spam" |
| message | string | 消息内容 |
| apiUser | string | API_USER |
| category | string | 同 apiUser |
| labelId | int | 自定义的标签ID |
| labelName | string | 自定义的标签名称 |
| sendTags | list | 本次发送所使用的tag数组 |
| emailId | string | 每封email的唯一ID |
| recipient | string | 收信人 |
| timestamp | long | 时间戳 |
| token | string | 随机产生的长度为50的字符串 |
| signature | string | 签名字符串 |
| userHeaders | string | 客户调用发送接口自定义的, 并且以"SC-Custom-"开头的头部信息 |
无效邮件 ( invalid )
参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
| event | string | 事件类型:"invalid" |
| message | string | 消息内容 |
| apiUser | string | API_USER |
| category | string | 同 apiUser |
| labelId | int | 自定义的标签ID |
| labelName | string | 自定义的标签名称 |
| sendTags | list | 本次发送所使用的tag数组 |
| maillistTaskId | long | 如果使用地址列表发送,将产生任务id |
| mail_list_task_id | long | 同 maillistTaskId |
| emailId | string | 每封email的唯一ID |
| recipient | string | 收信人 |
| timestamp | long | 时间戳 |
| token | string | 随机产生的长度为50的字符串 |
| signature | string | 签名字符串 |
| userHeaders | string | 客户调用发送接口自定义的, 并且以"SC-Custom-"开头的头部信息 |
| subStatDesc | string | 无效子类描述,eg:IP、域名被拒(5.7.1 Unfortunately, messages...protection.outlook.com]) |
| subStat | Integer | 无效子类,eg: 405 |
| cause | string | 原始拒信明细信息,eg: 5.7.1 Unfortunately, messages...protection.outlook.com] |
无效子类返回码和描述:
| subStat | subStatDesc |
|---|---|
| 401 | 在SendCloud黑名单中 |
| 402 | 取消订阅 |
| 403 | 服务器不可达 |
| 404 | 地址格式错误 |
| 405 | IP、域名被拒 |
| 406 | 地址不存在 |
| 407 | 垃圾邮件 |
| 408 | 发件人/收件人被拒 |
| 409 | 其他 |
软退信 ( soft_bounce )
参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
| event | string | 事件类型:"soft_bounce" |
| apiUser | string | API_USER |
| category | string | 同 apiUser |
| labelId | int | 自定义的标签ID |
| labelName | string | 自定义的标签名称 |
| sendTags | list | 本次发送所使用的tag数组 |
| maillistTaskId | long | 如果使用地址列表发送,将产生任务id |
| mail_list_task_id | long | 同 maillistTaskId |
| emailId | string | 每封email的唯一ID |
| recipient | string | 收信人 |
| timestamp | long | 时间戳 |
| token | string | 随机产生的长度为50的字符串 |
| signature | string | 签名字符串 |
| userHeaders | string | 客户调用发送接口自定义的, 并且以"SC-Custom-"开头的头部信息 |
| subStatDesc | string | 软退信子类描述,eg:其他(DIAGNOSTIC-CODE...83.) |
| subStat | Integer | 软退信子类,eg: 509 |
| cause | string | 原始软退信明细信息,eg: DIAGNOSTIC-CODE: smtp; Your email to group .....https://support.google.com/a/answer/168383. |
软退信子类返回码和描述:
| subStat | subStatDesc |
|---|---|
| 503 | 服务不可达 |
| 505 | ip或域名被拒 |
| 506 | 邮箱地址不存在 |
| 507 | 垃圾邮件 |
| 508 | 发件人/收件人被拒 |
| 509 | 其他 |
转信 ( route )
参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
| event | string | 事件类型:"route" |
| message | string | 消息内容 "mx route" |
| timestamp | long | 时间戳 |
| from | string | 邮件标头发件人,声称发件人地址,显示在正文的发件人 |
| fromname | string | 发件人名称 |
| to | string | 邮件标头收件人 |
| toname | string | 收件人名称 |
| x_mx_mailfrom | string | 信封发件人,实际发件人 |
| x_mx_rcptto | string | 实际收件人地址 |
| headers | string | 邮件头部信息. JSON 格式 |
| html | string | 转信邮件 html 格式的内容 |
| text | string | 转信邮件文本格式的内容 |
| subject | string | 标题 |
| raw_message_url | string | 转信文件下载. eg:https:xxx.eml,链接有效期为15天.JAVA解析eml文件示例 |
| raw_message | string | 邮件原文 |
| token | string | 随机产生的长度为50的字符串 |
| signature | string | 签名字符串 |
| userHeaders | string | 客户调用发送接口自定义的, 并且以"SC-Custom-"开头的头部信息 |
| reference | string | 如果有值,为: SendCloud发出邮件的Message-ID |
| emailId | string | 父邮件的唯一id. 通过此字段,回复邮件可与父邮件进行关联。此值从reference和In-Reply-To中解析 |
| labelId | int | 父邮件自定义的标签ID |
| labelName | string | 父邮件自定义的标签名称 |
Note:
- 当您未传入自定义的Message-ID时,reference中的Message-ID按平台规则自动生成,Message-ID的前缀与emailid的前缀相同。当您通过SMTP请求传入自定义的Message-ID时,reference中的Message-ID将为您传入的Message-ID。当回复邮件匹配不上父邮件时,会为空。
- eg:"reference":"1644468027883_1024_25239_6195.sg-10_1_253_1-inbound0@ifaxin.com" #ifaxin.com为发信域名