Listmonk 6.2.0 快速入门与集成示例

Listmonk 6.2.0 快速入门实操:创建邮件列表、API 与 CSV 批量导入订阅者、配置 Mailpit SMTP 发信通道、创建并发送 Campaign、验证邮件送达与 RFC 8058 一键退订头,并汇总 API 集成要点

ListmonkListmonkLinuxDocker Compose2026/10/17

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/listsPOST创建列表返回新列表 id
/api/listsGET列表清单返回全部列表
/api/subscribersPOST创建单个订阅者返回订阅者 id
/api/subscribers?list_id=NGET按列表查订阅者total: 5
/api/import/subscribersPOSTCSV 异步导入status: importing
/api/import/subscribers/logsGET导入日志imported finished
/api/settingsGET / PUT读/全量写运行时设置SMTP 写入 200
/api/settings/smtp/testPOSTSMTP 连通性测试200 + 测试邮件实收
/api/campaignsPOST创建 Campaignstatus: draft
/api/campaigns/:id/statusPUT置 running 触发发送数秒后 finished
/api/campaigns/:idGET查询投递进度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 可直接生效)。

十、参考链接