邮件
用 CZL Connect 的发件通道给你的用户发邮件,不用自己维护 SMTP、发件域名与退订。三种发法:
| 发法 | 收件人 | 内容 | 调用方式 |
|---|---|---|---|
| 验证码 | 任意邮箱 | 固定模板,验证码由 CZL Connect 生成与校验 | API (email:code) |
| 自定义邮件 | 本应用用户 | HTML 或 Markdown,自动附退订链接 | API (email:send) |
| 群发通知 | 本应用用户 + 手动添加的邮箱 | Markdown,可定时 | 应用详情「邮件」或 API (broadcast:write) |
API 调用方式与凭证见应用级 API:在应用详情 → API 接入新建凭证并勾选对应 scope,请求头带 Authorization: Bearer czlc_live_...,接口前缀 https://connect.czl.net/api/v1/app。
开通条件与额度
按应用所有者的套餐开放,三种发法共用账户的月度邮件额度 (账户内所有应用合计,自然月按北京时间):
| 套餐 | 月度邮件额度 | 每日验证码上限 |
|---|---|---|
| Lite | 不支持 | 不支持 |
| Plus | 5,000 封 | 2,000 封 |
| Pro | 30,000 封 | 10,000 封 |
| Enterprise | 不限 | 50,000 封 |
验证码发送成功才计数;自定义邮件与群发按进入发送队列的收件人数计,发送失败不退还。
发件地址
邮件的显示名为「应用名(经 CZL Connect)」,发件地址默认 [email protected]。在应用详情 → 邮件设置发件别名后,地址变为 czlconnect_<别名>@notify.czl.net,收件人可按地址区分不同应用。
- 别名 3-20 位小写字母、数字或连字符,全站唯一
- 套餐支持邮件功能 (Plus 及以上) 才能设置,设置需通行密钥验证
- 别名只能设置一次,设置后不能自行修改或清除;确需更改请联系客服。改掉的旧别名为原应用保留 90 天,期间其它应用不能使用
- 改地址后,收件人按旧地址设置的过滤规则与白名单会失效
- 不能包含冒充官方职能或知名品牌的词 (如
admin、billing、security、apple、paypal) - 发件地址不收信,不要引导用户回复邮件
验证码
适合给还不是你用户的人发验证码:临时用户订阅前确认是真人、注册前验证邮箱、注销账号前二次确认。验证码由 CZL Connect 生成、发送并校验,你的后端只拿到一个验证码 id。
发送
POST /api/v1/app/email-codes
Authorization: Bearer czlc_live_...
Content-Type: application/json
{
"email": "[email protected]",
"purpose": "subscribe",
"locale": "zh",
"clientIp": "203.0.113.9"
}
| 字段 | 必填 | 说明 |
|---|---|---|
email | 是 | 收件邮箱,任意地址 |
purpose | 否 | 用途,决定邮件主题与说明,默认 general,取值见下表 |
locale | 否 | 邮件语言 zh / en,默认 zh |
clientIp | 建议 | 发起请求的终端用户 IP,用于按 IP 限频;只认公网地址,不传则不做 IP 维度限频 |
purpose | 邮件主题 |
|---|---|
general | 「应用名」验证码 |
signup | 「应用名」注册验证码 |
login | 「应用名」登录验证码 |
subscribe | 「应用名」订阅确认验证码 |
delete_account | 「应用名」注销账号验证码 (正文提醒操作不可撤销) |
邮件同步发送,邮局收下后才返回 (通常一两秒),不排队:
{ "id": "ec_Xh3...", "expiresIn": 600, "expiresAt": "2026-11-14T10:20:00+08:00" }
验证码为 6 位数字,10 分钟内有效。
校验
POST /api/v1/app/email-codes/verify
Content-Type: application/json
{ "id": "ec_Xh3...", "code": "482913" }
{ "verified": true, "email": "[email protected]", "purpose": "subscribe" }
校验通过后验证码立即作废。错误返回 code_mismatch 并带 attemptsRemaining;错满 5 次作废,需重新发送。以返回的 email 为准记录用户邮箱,不要信任前端提交的邮箱。
限频
| 维度 | 上限 |
|---|---|
| 同一应用、同一收件邮箱 | 1 分钟 1 封 |
| 同一收件邮箱 (所有应用合计) | 24 小时 10 封 |
同一应用、同一 clientIp | 每小时 20 封 |
| 账户 (所有应用合计) | 每日验证码上限,见套餐表 |
界面上的「重新发送」按钮建议倒计时 60 秒。少数邮箱服务商会对首次来信做灰名单延迟,几分钟后才到;验证码页面请提示用户检查垃圾箱。
自定义邮件
给本应用用户发通知 (订单状态、账户提醒等),正文由你决定。
POST /api/v1/app/emails
Content-Type: application/json
{
"userIds": ["1024", "2048"],
"subject": "你的订单已发货",
"html": "<p>订单 <b>#1234</b> 已发货。</p>"
}
| 字段 | 说明 |
|---|---|
userIds | 本应用用户 ID (字符串或数字),单次 1-100 个 |
subject | 主题,不超过 200 字 |
html / markdown | 正文,二选一,不超过 200 KB |
HTML 会经过净化:保留内联样式、表格布局与 http(s) 图片和链接,去掉脚本、表单、iframe 与事件属性。邮件外壳由 CZL Connect 添加 (应用名、收件原因、退订链接),并带 List-Unsubscribe 一键退订头。
返回 202,逐个给出受理结果,受理的邮件立即进入发送队列,一般数秒内送出:
{
"queued": 1,
"results": [
{ "userId": "1024", "status": "queued" },
{ "userId": "2048", "status": "skipped", "reason": "unsubscribed" }
]
}
reason | 说明 |
|---|---|
not_app_user | 不是本应用用户 |
banned | 全站封禁或在本应用中停用 |
unsubscribed | 已退订本应用通知 |
email_unavailable | 没有已验证的真实邮箱 (如 QQ / 微信登录且未补登邮箱) |
rate_limited | 今天给该用户发的自定义邮件已达 20 封 |
用户退订后,群发与自定义邮件都不再发给他;验证码不受退订影响。服务重启时尚未发出的自定义邮件可能丢失,对必达内容请以你自己的业务状态为准。
群发任务
与应用详情「邮件」里新建群发的规则一致:创建时生成收件人快照,发送时再次排除退订与封禁用户,可定时,未开始的任务可取消。
POST /api/v1/app/broadcasts
Content-Type: application/json
{
"subject": "11 月更新",
"markdown": "## 新功能\n\n- ...",
"audience": "all",
"groups": ["t1", "t2"],
"scheduledAt": "2026-11-20T10:00:00+08:00"
}
| 字段 | 说明 |
|---|---|
audience | all 全部用户 / single 单个用户 (配 userId) / exclude 排除部分用户 (配 excludeUserIds) / extra_only 只发手动添加的邮箱 |
groups | all / exclude 时按应用内分组筛选,空为全部 |
extraEmails | 额外的外部邮箱 (未经 CZL Connect 登录的用户),单任务最多 1000 个,计入额度 |
scheduledAt | RFC 3339 定时发送时间,空为立即 (由每分钟一次的发送任务拾取) |
其它端点:
| 端点 | 说明 |
|---|---|
GET /broadcasts?page=&pageSize= | 任务列表,按创建时间倒序 |
GET /broadcasts/:broadcastId | 任务状态与计数 (sentCount / failedCount / skippedCount) |
POST /broadcasts/:broadcastId/cancel | 取消尚未开始发送的任务 |
额度查询
GET /api/v1/app/mail/quota
需要 email:code、email:send、broadcast:write 任一 scope,返回发件地址、每日验证码上限、本应用今日 / 本月用量与账户月度额度。
错误码
错误响应形如 {"error": "<code>", "message": "<说明>"}:
| HTTP | error | 说明 |
|---|---|---|
| 400 | invalid_request / invalid_email / invalid_purpose | 参数不合法 |
| 400 | code_mismatch | 验证码错误,带 attemptsRemaining |
| 400 | code_expired | 验证码不存在、已过期或已使用 |
| 400 | code_attempts_exceeded | 错误次数过多,验证码已作废 |
| 403 | mail_disabled | 当前套餐不支持 |
| 403 | quota_exceeded | 月度邮件额度用完 |
| 429 | rate_limited | 触发收件邮箱或 IP 限频 |
| 429 | daily_limit_exceeded | 账户今日验证码已达套餐上限 |
| 502 | send_failed | 邮件未能送出,可稍后重试 |
| 503 | temporarily_unavailable | 服务繁忙,稍后重试 |