Listmonk 6.2.0 快速入门与集成示例
Listmonk 6.2.0 快速入门实操:创建邮件列表、API 与 CSV 批量导入订阅者、配置 Mailpit SMTP 发信通道、创建并发送 Campaign、验证邮件送达与 RFC 8058 一键退订头,并汇总 API 集成要点
Listmonk 6.2.0 快速入门与集成示例
目标:在一个已部署的 Listmonk 6.2.0 环境上,完整走通「创建邮件列表 → 添加订阅者(API 单建 + CSV 批量导入)→ 配置发信通道 → 创建 Campaign → 发送并验收邮件」的核销闭环,并汇总本文涉及的 API 集成要点。本文所有命令与回显均在 Listmonk v6.2.0 环境实测通过(该环境自 v6.1.0 原地升级而来,升级过程与数据完好性验证见《进阶实战与运维》)。
一、环境信息
- 部署形态:Docker Compose 最小可用环境——Listmonk 主程序 + PostgreSQL 数据库 + Mailpit 本地邮件抓信服务(部署方法与 compose 文件详解见《Docker Compose 部署指南》)
- Listmonk 版本:v6.2.0(镜像
listmonk/listmonk:v6.2.0,该环境自 v6.1.0 原地升级而来) - 服务端口:9000(Listmonk Web 与 API);Mailpit SMTP 1025 收信、Web 收件箱 8025
- 管理员账号:由首次启动的环境变量自动创建(演示:
admin/q1w2e3r4)
二、环境准备
2.1 环境说明
本章使用 Docker Compose 部署一套最小可用环境:Listmonk 主程序 + PostgreSQL 数据库 + Mailpit 本地邮件抓信服务(用于替代真实 SMTP 服务器验收邮件,避免向真实邮箱发信)。部署方法与 compose 文件详解见《Docker Compose 部署指南》,在此基础上追加 mailpit 服务即可:
mailpit:
image: axllent/mailpit:latest
container_name: ops_mailpit
restart: unless-stopped
ports:
- "8025:8025"
networks:
- listmonk
Mailpit 只需与 app 同网络(它接收 Listmonk 通过 1025 端口投递的邮件,并在 8025 端口提供 Web 收件箱界面;不映射宿主机端口也可,这里映射 8025 便于浏览器查看)。
💡 技术拓展:Mailpit 是一款开源自托管的邮件测试工具,内置 SMTP 服务端(默认 1025 端口接收)与 Web 收件箱(默认 8025 端口查看),收到的邮件不会真正投递到外部邮箱。开发与测试阶段用它验收邮件渲染与投递链路,是邮件系统联调的常用替代品。
启动后确认三个服务就绪:
docker compose ps
实测回显(节选,省略启动时长字段):
NAME IMAGE SERVICE STATUS PORTS
ops_listmonk_app listmonk/listmonk:v6.2.0 app Up 0.0.0.0:9000->9000/tcp
ops_mailpit axllent/mailpit:latest mailpit Up 0.0.0.0:8025->8025/tcp
数据库服务同样处于 Up 状态(此处节选未列出)。管理员账号由首次启动的环境变量自动创建(演示:admin / q1w2e3r4)。
2.2 创建 API 用户
后续操作统一走 REST API,先创建 API 用户。用管理员账号登录后台(或携带登录会话 Cookie 调用接口),请求体:
{
"username": "opsapi",
"name": "Ops API",
"type": "api",
"status": "enabled",
"user_role_id": 1
}
实测响应(节选,data.password 即 API Token,仅在创建时完整返回一次):
{"data":{"id":2,"username":"opsapi","password":"BRZCE2IOv0MvU0sk51jcaUhWERbwIzQJ","name":"Ops API","type":"api","status":"enabled","user_role":{"id":1,"name":"Super Admin","permissions":["webhooks:post_bounce","users:manage","tx:send","media:manage","settings:manage","settings:maintain","campaigns:send","lists:manage_all","subscribers:get","subscribers:manage","bounces:manage","roles:get","roles:manage","settings:get","lists:get_all","subscribers:get_all","campaigns:get_all","campaigns:manage_all","media:get","templates:get","templates:manage","users:get","subscribers:import","subscribers:sql_query","campaigns:get","campaigns:get_analytics","campaigns:manage","bounces:get"]}}}
后续示例统一使用 curl -u "opsapi:<TOKEN>" 进行 Basic 认证。注意 API Token 不是网页登录密码——用登录密码调用 API 会返回 403 invalid API credentials。
三、第一步:创建邮件列表
创建一个名为「产品周报」的私有列表(单步确认),打上 newsletter 标签:
curl -s -u "opsapi:BRZCE2IOv0MvU0sk51jcaUhWERbwIzQJ" \
-H "Content-Type: application/json" \
-d '{"name":"产品周报","type":"private","optin":"single","tags":["newsletter"]}' \
http://localhost:9000/api/lists
实测回显(节选):
{"data":{"id":3,"name":"产品周报","type":"private","optin":"single","tags":["newsletter"],...}}
返回的 id: 3 即新列表 ID(id 1、2 为安装自带的 Default list 与 Opt-in list)。查询确认:
curl -s -u "opsapi:BRZCE2IOv0MvU0sk51jcaUhWERbwIzQJ" http://localhost:9000/api/lists
实测返回三个列表(节选):
id=1 Default list(private/single)
id=2 Opt-in list(public/double)
id=3 产品周报(private/single, tags=newsletter)
四、第二步:添加订阅者
4.1 API 创建单个订阅者
curl -s -u "opsapi:BRZCE2IOv0MvU0sk51jcaUhWERbwIzQJ" \
-H "Content-Type: application/json" \
-d '{"email":"zhang.wei@example.com","name":"张伟","lists":[3],"preconfirm_subscriptions":true,"status":"enabled"}' \
http://localhost:9000/api/subscribers
实测回显(节选):
{"data":{"id":3,"email":"zhang.wei@example.com","name":"张伟","status":"enabled",...}}
说明:lists:[3] 把订阅者直接加入「产品周报」;preconfirm_subscriptions: true 表示跳过确认流程直接订阅生效(适合自己导入的存量用户)。
4.2 CSV 批量导入
准备 CSV 文件(首行为表头,email 列必需):
cat > /tmp/subs.csv <<'EOF'
email,name
li.na@example.com,李娜
wang.fang@example.com,王芳
zhao.lei@example.com,赵磊
sun.qiang@example.com,孙强
EOF
调用导入接口(multipart 表单,params 是 JSON 参数、file 是 CSV 文件):
curl -s -u "opsapi:BRZCE2IOv0MvU0sk51jcaUhWERbwIzQJ" \
-F 'params={"mode":"subscribe","delim":",","lists":[3]};type=application/json' \
-F "file=@/tmp/subs.csv;type=text/csv" \
http://localhost:9000/api/import/subscribers
实测回显:
{"data":{"name":"subs.csv","total":0,"imported":0,"status":"importing"}}
响应中 status: importing 说明导入是异步任务,立即返回并不代表完成。查询导入日志确认结果:
curl -s -u "opsapi:BRZCE2IOv0MvU0sk51jcaUhWERbwIzQJ" http://localhost:9000/api/import/subscribers/logs
实测回显(节选):
processing 'subs.csv' ... imported finished
⚠️ 踩坑实录:
params中的分隔符参数名是delim而不是delimiter。写成后者会报错"Delimiter should be a single character."。mode取值只有两个:subscribe(正常订阅导入)与blocklist(导入即拉黑)。
4.3 导入结果验证
按列表筛选订阅者:
curl -s -u "opsapi:BRZCE2IOv0MvU0sk51jcaUhWERbwIzQJ" "http://localhost:9000/api/subscribers?list_id=3"
实测回显(节选):
{"data":{"total":5,...}}
total: 5——API 单建的「张伟」加上 CSV 导入的 4 人,共 5 名订阅者已在「产品周报」列表中。
五、第三步:配置发信通道(Mailpit)
5.1 写入 SMTP 设置
v6 版本的 SMTP 配置存放在数据库运行时设置(Settings)中,通过 PUT /api/settings 全量回传修改。实际操作脚本如下(先 GET 拉取当前设置、修改 smtp[0]、再整体 PUT 回去):
import json, urllib.request, base64
BASE = "http://localhost:9000"
TOK = base64.b64encode(b"opsapi:BRZCE2IOv0MvU0sk51jcaUhWERbwIzQJ").decode()
def req(method, path, body=None):
r = urllib.request.Request(BASE + path, method=method)
r.add_header("Authorization", "Basic " + TOK)
data = None
if body is not None:
data = json.dumps(body).encode()
r.add_header("Content-Type", "application/json")
try:
with urllib.request.urlopen(r, data) as resp:
return resp.status, json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
return e.code, json.loads(e.read().decode())
st, cur = req("GET", "/api/settings")
cur = cur["data"]
cur["smtp"][0].update({
"enabled": True, "host": "mailpit", "port": 1025,
"auth_protocol": "", "username": "", "password": "",
"max_conns": 2, "tls_type": "none",
})
st, out = req("PUT", "/api/settings", cur)
print("PUT settings:", st, json.dumps(out))
st, cur2 = req("GET", "/api/settings")
s = cur2["data"]["smtp"][0]
print("smtp[0]:", {k: s[k] for k in ("enabled", "host", "port", "auth_protocol", "tls_type")})
实测输出:
PUT settings: 200 {"data": true}
smtp[0]: {'enabled': True, 'host': 'mailpit', 'port': 1025, 'auth_protocol': '', 'tls_type': 'none'}
设置写入成功并回读确认。由于 Mailpit 与 Listmonk 同处一个 compose 网络,主机名直接填服务名 mailpit、端口 1025、免认证免 TLS。
⚠️ 版本差异:v6 起 SMTP 配置只能存这里——实测在 compose 中注入
LISTMONK_smtp__*环境变量不会生效(详见《配置参数详解》第七章)。
5.2 SMTP 连通性测试
保存设置后先做一次连通性测试(POST /api/settings/smtp/test,请求体为完整 SMTP 配置加一个 email 收件地址字段):
curl -s -o /dev/null -w "%{http_code}\n" \
-u "opsapi:BRZCE2IOv0MvU0sk51jcaUhWERbwIzQJ" \
-H "Content-Type: application/json" \
-d '{"host":"mailpit","port":1025,"auth_protocol":"","enabled":true,"tls_type":"none","username":"","password":"","email":"admin@listmonk.local"}' \
http://localhost:9000/api/settings/smtp/test
实测回显:
200
打开 Mailpit 收件箱 http://<服务器IP>:8025,实测收到一封收件人为 admin@listmonk.local、主题为 Test connection 的测试邮件,SMTP 链路验收通过。
六、第四步:创建营销活动(Campaign)
Campaign 是 Listmonk 的群发单元,绑定一个或多个列表,正文支持 Go template 变量渲染:
curl -s -u "opsapi:BRZCE2IOv0MvU0sk51jcaUhWERbwIzQJ" \
-H "Content-Type: application/json" \
-d '{"name":"第 1 期","subject":"产品周报 · 第 1 期","lists":[3],"from_email":"noreply@listmonk.local","content_type":"html","body":"<h1>你好 {{ .Subscriber.Name }}</h1><p>这是第 1 期产品周报。</p>"}' \
http://localhost:9000/api/campaigns
实测回显(节选):
{"data":{"id":2,"name":"第 1 期","subject":"产品周报 · 第 1 期","status":"draft",...}}
Campaign 创建后处于 draft 状态,id: 2。正文中的 {{ .Subscriber.Name }} 是模板变量,发送时会替换为每位订阅者的姓名。
七、第五步:发送活动并验收
7.1 启动发送
把 Campaign 状态置为 running 即触发投递:
curl -s -u "opsapi:BRZCE2IOv0MvU0sk51jcaUhWERbwIzQJ" \
-X PUT -H "Content-Type: application/json" \
-d '{"status":"running"}' \
http://localhost:9000/api/campaigns/2/status
数秒后查询活动状态:
curl -s -u "opsapi:BRZCE2IOv0MvU0sk51jcaUhWERbwIzQJ" http://localhost:9000/api/campaigns/2
实测回显(节选):
{"data":{"id":2,"status":"finished","to_send":5,"sent":5,...}}
status: finished、to_send: 5、sent: 5——5 名订阅者全部投递完成。
7.2 验证邮件送达
打开 Mailpit 收件箱,实测共 5 封新邮件(total: 5、unread: 5),主题均为 产品周报 · 第 1 期。查看其中收件人为 sun.qiang@example.com 的邮件 HTML 源码,正文标题渲染结果:
<h1>你好 孙强</h1>
模板变量 {{ .Subscriber.Name }} 已按订阅者姓名(孙强)逐人渲染,个性化投递验证通过。
7.3 退订头检查
查看任一封邮件的邮件头,实测包含两组退订相关头(节选):
List-Unsubscribe: <http://<root_url>/subscription/<campaign-uuid>/<subscriber-uuid>>
List-Unsubscribe-Post: List-Unsubscribe=One-Click
💡 技术拓展:
List-Unsubscribe-Post: List-Unsubscribe=One-Click是 RFC 8058 定义的一键退订标准——主流邮箱服务商(Gmail、Outlook 等)会在邮件界面展示「退订」按钮并直接以 POST 方式退订,无需用户跳转网页。Listmonk 为每封 Campaign 邮件自动生成携带 campaign UUID 与 subscriber UUID 的一次性退订链接,这是提升邮件信誉度、避免进垃圾箱的关键合规头。
八、API 集成要点汇总
本文实测涉及的 API 全景(认证方式均为 Basic:API 用户名 + API Token):
| 端点 | 方法 | 用途 | 本文实测结果 |
|---|---|---|---|
/api/lists | POST | 创建列表 | 返回新列表 id |
/api/lists | GET | 列表清单 | 返回全部列表 |
/api/subscribers | POST | 创建单个订阅者 | 返回订阅者 id |
/api/subscribers?list_id=N | GET | 按列表查订阅者 | total: 5 |
/api/import/subscribers | POST | CSV 异步导入 | status: importing |
/api/import/subscribers/logs | GET | 导入日志 | imported finished |
/api/settings | GET / PUT | 读/全量写运行时设置 | SMTP 写入 200 |
/api/settings/smtp/test | POST | SMTP 连通性测试 | 200 + 测试邮件实收 |
/api/campaigns | POST | 创建 Campaign | status: draft |
/api/campaigns/:id/status | PUT | 置 running 触发发送 | 数秒后 finished |
/api/campaigns/:id | GET | 查询投递进度 | to_send: 5 / sent: 5 |
集成注意事项:
- 导入是异步的:
/api/import/subscribers返回importing后需轮询 logs 接口确认imported finished; - Campaign 状态机:
draft → running → finished,通过PUT /api/campaigns/:id/status推进;发送中可置回paused暂停; - 设置必须全量回传:
PUT /api/settings要求先 GET 拿到完整设置对象、修改目标键后整体 PUT,直接只传局部键会覆盖丢失其他设置; - 错误定位:API 报错均返回带
message的 JSON(如导入参数错误返回"Delimiter should be a single character."),便于程序化处理。
九、常见问题
9.1 导入接口报 Delimiter should be a single character
params 里的分隔符参数名写错。正确字段名是 delim(单字符,如 ","),不是 delimiter:
{"mode":"subscribe","delim":",","lists":[3]}
9.2 Campaign 一直卡在 running 且无人收到邮件
发信通道未配置或不可达。按第五章确认 Settings 中 SMTP 已启用、host/port 正确,并用 /api/settings/smtp/test 验证连通后再重试。测试环境优先用 Mailpit 排除真实 SMTP 服务商的认证与限流干扰。
9.3 API 返回 403 invalid API credentials
把登录密码当成了 API Token。API 认证凭据是「API 用户名 + 创建时返回的 48 位 Token」;Token 只在创建响应中完整出现一次,遗失后需重建 API 用户。
9.4 订阅者导入后收不到 Campaign
检查订阅者是否已在目标列表(GET /api/subscribers?list_id=N),以及订阅者状态是否为 enabled、订阅是否已确认(double opt-in 列表下 unconfirmed 状态的订阅者不会收到邮件;导入时用 mode: subscribe 且列表为 single opt-in 可直接生效)。
十、参考链接
- Listmonk API 文档:https://listmonk.app/docs/apis/
- Campaign 与模板文档:https://listmonk.app/docs/templating/
- Mailpit 官网:https://mailpit.axllent.org/
- RFC 8058(One-Click 退订):https://datatracker.ietf.org/doc/html/rfc8058
- GitHub 仓库:https://github.com/knadh/listmonk