Gotify 3.1.1 API 使用详解
Gotify 3.1.1 CLI 与 REST API/WebSocket 全域实测:三子命令、22 条端点总览、令牌三形态与会话提权闭环、令牌轮换陷阱、消息推送分页删除、WebSocket 实时推送验证、优先级语义与 FAQ。
Gotify 3.1.1 API 使用详解
目标:把 Gotify 3.1.1 的两个程序化入口讲全——CLI 三子命令与 REST API/WebSocket 全域端点,认证三形态、令牌轮换、会话提权、消息分页与优先级语义均以逐条 curl 实测闭环呈现。本文全部命令与回显在 Ubuntu 22.04(x86_64)实测环境运行,服务版本
/version实测为 3.1.1,实测端口 8144(8128-8152 槽位约定;标准默认端口 80)。安装与配置基础见 《Ubuntu 22.04 安装 Gotify 3.1.1 指南》 与 《Gotify 3.1.1 配置参数详解》,组件定位见 《Gotify 3.1.1 介绍与选型指南》。
一、环境信息
- 操作系统:Ubuntu 22.04.4 LTS(Jammy Jellyfish),x86_64;
- 服务版本:Gotify 3.1.1(2026-09-15 发布,安装篇所装,与本文实验同版本);
- 运行形态:官方二进制
/opt/gotify/gotify serve+ systemd(服务名gotify); - 实测地址:
http://127.0.0.1:8144; - 初始管理员:admin(Basic Auth 口令为安装时设定值);
- 实验账号链:会话登录 → 首个 client(ops-cli)→ 二级 client(android-phone / low-priv-cli)→ 应用令牌(demo-app);
- API 定义来源:运行实例自带的 Swagger 页面
http://127.0.0.1:8144/swagger(OpenAPI 2.0 规范,22 条路径),官方在线文档 gotify.net/docs/rest-api 与 gotify.net/api-docs; - 本文令牌均为实验环境即抛值,读者在自己实例上应使用自建令牌。
二、CLI 面:三个子命令
Gotify 3.0.0 起 CLI 随二进制发行,gotify --help 实测(无参数时打印总帮助):
/opt/gotify/gotify --help
实测回显:
Usage: gotify [flags] <command> [arguments]
Commands:
serve Start the Gotify server.
migrate-config <file.yml> Convert an old YAML config file to the new env
format and print it to stdout.
version Show version information
2.1 gotify version
打印版本、构建信息与 Go 模块依赖清单(安装篇 4.4 节有完整节选),快速核验二进制版本:
/opt/gotify/gotify version | head -5
实测回显:
Version: 3.1.1
Commit: e6361caa512547052b1013dbf4544e6d7ff32604
Build Date: 2026-09-15-18:26:53
Go Build Info:
go go1.26.0
2.2 gotify serve 与 --help 陷阱
serve 启动服务进程(systemd 单元的 ExecStart 即 gotify serve)。注意 serve 不解析 --help,加上 --help 会直接把服务拉起来:
/opt/gotify/gotify serve --help
实测回显(root 会话前台执行,默认绑 80 已被占用):
2026-10-21T19:47:09+08:00 INF Gotify build_date=2026-09-15-18:26:53 version=3.1.1
2026-10-21T19:47:09+08:00 ERR Server error error="listen tcp :80: bind: address already in use"
排障时查命令面用 gotify --help(无子命令的总帮助),不要给 serve 传 --help;改监听端口靠 GOTIFY_SERVER_PORT 环境变量而非命令行参数(配置篇 5.1 节)。
2.3 gotify migrate-config
2.x → 3.x 配置迁移子命令:读取旧 config.yml,把等价环境变量打印到 stdout(19 行 GOTIFY_* 全量回显见安装篇 8.5 节),不修改旧文件。语法:
/opt/gotify/gotify migrate-config /etc/gotify/config.yml
CLI 面小结:3.1.1 的 CLI 只有三个子命令,运维动作(改配置、轮换令牌、管理用户)全部走环境变量 + REST API,这也是 3.x「CLI 薄、API 厚」的形态。
三、REST API 总览
3.1 端点全景
以下 22 条路径取自 3.1.1 实例自带的 OpenAPI 2.0 规范(/swagger 页内嵌 spec),按域分组:
| 域 | 方法与路径 | 语义 |
|---|---|---|
| 系统 | GET /health | 健康检查(无认证) |
| 系统 | GET /version | 版本信息(无认证) |
| 认证 | POST /auth/local/login | 本地登录换会话 |
| 认证 | POST /logout | 注销当前会话 |
| 用户 | GET /current/user | 当前用户信息(含服务开关状态) |
| 用户 | PUT /current/user/password | 改自己口令 |
| 用户 | GET/POST /user · GET/PUT/DELETE /user/{id} | 用户管理(需提权会话或 Basic Auth) |
| 客户端 | GET/POST /client · PUT/DELETE /client/{id} | 接收端客户端管理 |
| 客户端 | POST /client/{id}/elevate | 会话提权(限时) |
| 应用 | GET/POST /application · PUT/DELETE /application/{id} | 发送端应用管理 |
| 应用 | GET /application/{id}/image · PUT /application/{id}/image | 应用图标 |
| 应用 | GET /application/{id}/message | 该应用的消息列表 |
| 应用 | PUT /application/{id}/security | 令牌轮换 |
| 消息 | POST /message | 推送消息(应用令牌) |
| 消息 | GET /message · DELETE /message/{id} | 查询/删除消息(客户端令牌) |
| 流 | GET /stream(WebSocket 升级) | 实时消息流 |
| 插件 | GET /plugin · GET /plugin/{id}/config | 插件列表/配置 |
| 插件 | POST /plugin/{id}/disable · /enable · /display | 插件开关与展示 |
3.2 认证方式定义
spec 的 securityDefinitions 共 7 种,归为三类:
| 类别 | 方式 | 说明 |
|---|---|---|
| 应用令牌 | Authorization: Bearer Axxx / X-Gotify-Key: Axxx / ?token=Axxx | 用于推送(POST /message)与应用管理 |
| 客户端令牌 | Authorization: Bearer Cxxx / X-Gotify-Key: Cxxx / ?token=Cxxx | 用于查询/删除消息、WebSocket 流、客户端与用户管理 |
| 用户凭据 | Basic Auth(用户名:口令) | 管理操作与会话登录;部分敏感端点仅认 Basic Auth 或提权会话 |
两类令牌前缀不同(3.x 新增):应用令牌 gtfya. 开头,客户端令牌 gtfyc. 开头——从令牌字面即可分辨类型,误用会在域检查层被拒(3.4 节实测)。
3.3 可视化调试入口
浏览器打开 http://<服务器>:8144/swagger/(注意尾斜杠)进入 Swagger UI,右上角 Authorize 填入令牌后可在页面内直接试调全部端点;本文所有 curl 实验的预期行为均与该页 spec 一致。
四、认证机制实测
4.1 令牌三形态对照
用同一个客户端令牌(gtfyc. 前缀)依次以三种方式调用 GET /message:
CT='gtfyc.xxxx'
curl -s -o /dev/null -w '%{http_code}\n' -H "Authorization: Bearer $CT" http://127.0.0.1:8144/message
curl -s -o /dev/null -w '%{http_code}\n' -H "X-Gotify-Key: $CT" http://127.0.0.1:8144/message
curl -s -o /dev/null -w '%{http_code}\n' "http://127.0.0.1:8144/message?token=$CT"
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8144/message
实测回显:
200
200
200
401
三种形态等价,无凭据 401。?token= 形态最方便但会进访问日志与浏览器历史,生产优先 Bearer 头。
4.2 会话登录:POST /auth/local/login
3.x 的登录端点需要 Basic Auth 承载用户名口令 + 表单字段 name 指定会话名(创建一个会话客户端);用 JSON body 直接提交会被拒:
# 错误姿势(JSON body):401
curl -s -w '\nHTTP:%%{http_code}\n' -X POST -H 'Content-Type: application/json' \
-d '{"username":"admin","password":"xxxx"}' http://127.0.0.1:8144/auth/local/login
实测回显:
{"error":"Unauthorized","errorCode":401,"errorDescription":"basic auth required"}
HTTP:401
正确姿势(-u Basic + formData):
curl -s -i -u admin:xxxx -d 'name=ops-cli' -X POST http://127.0.0.1:8144/auth/local/login | grep -iE '^(HTTP|Set-Cookie)'
实测回显:
HTTP/1.1 200 OK
Content-Type: application/json
Set-Cookie: gotify-client-token=gtfyc.xxxx; Path=/; Max-Age=604800; HttpOnly; SameSite=Strict
登录即创建了一个名为 ops-cli 的会话客户端,令牌同时通过 Set-Cookie 下发(默认有效期 604800 秒 = 7 天;响应体中也含同值 token)。配置篇 5.5 节已实测该 Cookie 默认无 Secure 标志,HTTPS 部署时打开 GOTIFY_SERVER_SECURECOOKIE=true。
注销会话用 POST /logout(携带会话凭据调用后,同凭据即失效):
curl -s -o /dev/null -w '%{http_code}\n' -X POST -H "Authorization: Bearer $CT" http://127.0.0.1:8144/logout
curl -s -w '\nHTTP %{http_code}\n' -H "Authorization: Bearer $CT" http://127.0.0.1:8144/current/user
实测回显:
200
{"error":"Unauthorized","errorCode":401,"errorDescription":"you need to provide a valid access token or user credentials to access this api"}
HTTP 401
4.3 令牌域隔离
应用令牌只能推消息、管理自身,客户端令牌才能查/删消息与流订阅;跨域调用被拒:
AT='gtfya.xxxx'
curl -s -H "Authorization: Bearer $AT" http://127.0.0.1:8144/client
实测回显:
{"error":"Unauthorized","errorCode":401,"errorDescription":"you need to provide a valid access token or user credentials to access this api"}
五、系统与元信息端点
5.1 GET /health(无认证)
curl -s http://127.0.0.1:8144/health; echo
实测回显:
{"health":"green","database":"green"}
两个分量分别表示服务与数据库健康态,监控探针直接用它。
5.2 GET /version(无认证)
curl -s http://127.0.0.1:8144/version; echo
实测回显:
{"version":"3.1.1","commit":"e6361caa512547052b1013dbf4544e6d7ff32604","buildDate":"2026-09-15-18:26:53"}
5.3 GET /current/user(需认证,返回服务开关)
curl -s -u admin:xxxx http://127.0.0.1:8144/current/user
实测回显:
{"id":1,"name":"admin","admin":true,"createdAt":"2026-10-21T19:54:33.694487038+08:00"}
用客户端令牌调用时,返回体在用户字段之外还带服务级开关状态(GotifyInfo 结构):
{"version":"3.1.1","register":false,"localAuth":true,"oidc":false,"oidcIdpName":"OIDC","oidcAutoRedirect":false}
register/localAuth/oidc 即配置篇 5.6 节讨论的三个开关的运行时视图,前端据此决定登录页形态。
六、client 域:接收端管理与会话提权
6.1 创建与列表
会话登录后创建第二个客户端(模拟手机端),并列出全部客户端:
curl -s -X POST -H "Authorization: Bearer $CT" -H 'Content-Type: application/json' \
-d '{"name":"android-phone"}' http://127.0.0.1:8144/client
实测回显:
{"id":2,"token":"gtfyc.9GSxjM5oFECLTGI8L-YRRxgcV4LRdIGYd-feBdaGFuw","name":"android-phone","createdAt":"2026-10-21T19:58:49.205016311+08:00","lastUsed":null,"expiresAfterInactivitySeconds":0}
curl -s -H "Authorization: Bearer $CT" http://127.0.0.1:8144/client
实测回显:
[{"id":1,"name":"ops-cli","createdAt":"2026-10-21T19:58:32.865008051+08:00","lastUsed":"2026-10-21T19:58:32.878656804+08:00","elevatedUntil":"2026-10-21T20:58:32.864485526+08:00","expiresAfterInactivitySeconds":604800,"expiresAt":"2026-10-28T19:58:32.878656804+08:00"},{"id":2,"name":"android-phone","createdAt":"2026-10-21T19:58:49.205016311+08:00","lastUsed":null,"expiresAfterInactivitySeconds":0}]
⚠️ 注意:
POST /client与POST /application的响应是令牌唯一一次明文出现的时刻——3.x 令牌入库即哈希,列表接口不再返回 token 字段(上表 id=1 的条目即无 token)。令牌务必落密钥管理,丢了只能轮换重建。
6.2 会话提权闭环(/client/{id}/elevate)
创建一个低权限客户端 low-priv-cli(id=3),试图用它创建用户:
curl -s -X POST -H "Authorization: Bearer $CT3" -H 'Content-Type: application/json' \
-d '{"name":"low-priv-cli"}' http://127.0.0.1:8144/client
curl -s -w '\nHTTP:%%{http_code}\n' -X POST -H "Authorization: Bearer $CT3" -H 'Content-Type: application/json' \
-d '{"name":"dev1","pass":"dev1pass"}' http://127.0.0.1:8144/user
实测回显(第二步):
{"error":"Forbidden","errorCode":403,"errorDescription":"session not elevated, use basic auth or call /client:elevate"}
HTTP:403
敏感操作(用户管理)要求 Basic Auth 或限时提权会话。给 low-priv-cli 提权,先故意漏掉 durationSeconds:
curl -s -w '\nHTTP:%%{http_code}\n' -X POST -H "Authorization: Bearer $CT3" \
-H 'Content-Type: application/json' -d '{"password":"xxxx"}' http://127.0.0.1:8144/client/3/elevate
实测回显:
{"error":"Bad Request","errorCode":400,"errorDescription":"Field 'durationSeconds' is required"}
HTTP:400
补全后重试(password 为 admin 口令,durationSeconds 单位秒):
curl -s -w '\nHTTP:%%{http_code}\n' -X POST -H "Authorization: Bearer $CT3" \
-H 'Content-Type: application/json' \
-d '{"password":"xxxx","durationSeconds":3600}' http://127.0.0.1:8144/client/3/elevate
实测回显:
HTTP:204
204 无响应体即提权成功。此时 GET /client/3 的回显中 elevatedUntil 已推进一小时(格式化展示):
{
"id": 3,
"name": "low-priv-cli",
"createdAt": "2026-10-21T20:00:24.609440591+08:00",
"lastUsed": "2026-10-21T20:00:24.650808914+08:00",
"elevatedUntil": "2026-10-21T21:01:09.296728896+08:00",
"expiresAfterInactivitySeconds": 0
}
提权后的会话即可执行用户管理(第八章实测)。默认提权窗口最长 1 小时,窗口内重复提权可续期;这是 3.x 针对会话劫持面新增的纵深防御。
七、application 域:发送端管理与令牌轮换
7.1 创建与更新
创建发送端应用 demo-app:
curl -s -X POST -H "Authorization: Bearer $CT" -H 'Content-Type: application/json' \
-d '{"id":0,"name":"demo-app","description":"演示应用"}' http://127.0.0.1:8144/application
实测回显:
{"id":1,"token":"gtfya.Ew-NsxQaf1KWMPyIehKFXKBGqBgZRldwSEv0BClTGKs","name":"demo-app","description":"演示应用","internal":false,"image":"static/defaultapp.png","defaultPriority":0,"createdAt":"2026-10-21T19:58:49.223754952+08:00","lastUsed":null,"sortKey":"a0"}
更新名称与默认优先级:
curl -s -X PUT -H "Authorization: Bearer $CT" -H 'Content-Type: application/json' \
-d '{"name":"demo-app","description":"演示应用(已更新)","defaultPriority":4}' \
http://127.0.0.1:8144/application/1
实测回显(关注 token 字段):
{"id":1,"token":"gtfya.cKm0_b-kaM83PTZPad6QX8RumExhM3wh03iawQ0De7Y","name":"demo-app","description":"演示应用(已更新)","internal":false,"image":"static/defaultapp.png","defaultPriority":4,"createdAt":"2026-10-21T19:58:49.223754952+08:00","lastUsed":"2026-10-21T19:59:16.417547789+08:00","sortKey":"a0"}
⚠️ 注意(重要陷阱):PUT /application/{id} 响应里的 token 是脱敏回显值,不是新令牌,也不能用。实测用旧 token 推送仍 200、用响应里的新值推送 401——该字段只是把库内哈希映射出的占位串回显出来,真实令牌并未轮换。要轮换必须走下一节的 security 端点。
7.2 令牌轮换闭环(PUT /application/{id}/security)
curl -s -X PUT -H "Authorization: Bearer $CT" -H 'Content-Type: application/json' \
-d '{"regenerateToken":true}' http://127.0.0.1:8144/application/1/security
实测回显:
{"regenerateToken":{"token":"gtfya.RY17B0UMyS0INH5_HdP32Xd6aMx3wQK57w60kYPCrY4"}}
这就是真轮换:旧令牌立即失效、新令牌此处明文返回一次。验证闭环——旧 token 推送 401、新 token 推送 200:
curl -s -o /dev/null -w '%{http_code}\n' -X POST -H "Authorization: Bearer gtfya.Ew-Nsx(旧)" \
-H 'Content-Type: application/json' -d '{"title":"旧token","message":"应401"}' http://127.0.0.1:8144/message
curl -s -H "Authorization: Bearer gtfya.RY17B0U(新)" -H 'Content-Type: application/json' \
-d '{"title":"轮换后推送","message":"regenerateToken 生效","priority":2}' http://127.0.0.1:8144/message
实测回显:
401
{"id":7,"appid":1,"message":"regenerateToken 生效","title":"轮换后推送","priority":2,"date":"2026-10-21T20:02:19.400400215+08:00"}
客户端令牌同理,PUT /client/{id}/security({"regenerateToken":true})完成轮换。
八、message 域:推送、分页与删除
8.1 推送(POST /message,应用令牌)
最小推送与富格式推送(extras 控制 WebUI 渲染与点击跳转):
AT='gtfya.xxxx'
curl -s -H "Authorization: Bearer $AT" -H 'Content-Type: application/json' \
-d '{"title":"部署完成","message":"Gotify 3.1.1 已在 8144 端口就绪","priority":5}' \
http://127.0.0.1:8144/message
curl -s -H "Authorization: Bearer $AT" -H 'Content-Type: application/json' \
-d '{"title":"磁盘告警","message":"/var 分区使用率 92%","priority":8,
"extras":{"client::display":{"contentType":"text/markdown"},
"client::notification":{"click":{"url":"https://example.com/monitor/disk"}}}}' \
http://127.0.0.1:8144/message
实测回显:
{"id":1,"appid":1,"message":"Gotify 3.1.1 已在 8144 端口就绪","title":"部署完成","priority":5,"date":"2026-10-21T19:59:16.41906769+08:00"}
{"id":2,"appid":1,"message":"/var 分区使用率 92%","title":"磁盘告警","priority":8,"extras":{"client::display":{"contentType":"text/markdown"},"client::notification":{"click":{"url":"https://example.com/monitor/disk"}}},"date":"2026-10-21T19:59:16.428773726+08:00"}
priority 语义(官方 gotify.net/docs/priority,查询日期 2026-10-21):Android 端 0 为无通知(仅应用内可见)、1-3 仅图标、4-7 追加声音、8-10 追加震动;WebUI 对 ≥4 的消息播放声音。上面的 5/8/2 正对应「常规提醒 / 高优告警 / 低优记录」三档。
8.2 列表与分页(GET /message,客户端令牌)
连推 5 条分页测试消息后,按 limit=2 查询:
curl -s -H "Authorization: Bearer $CT" 'http://127.0.0.1:8144/message?limit=2'
实测回显(最新在前):
{"paging":{"next":"/message?limit=2&since=4","size":2,"since":4,"limit":2},"messages":[{"id":5,"appid":1,"message":"分页测试第 5 条","title":"批量消息 5","priority":2,"date":"2026-10-21T20:00:11.411250626+08:00"},{"id":4,"appid":1,"message":"分页测试第 4 条","title":"批量消息 4","priority":2,"date":"2026-10-21T20:00:11.403137897+08:00"}]}
游标翻页直接用 paging.next 的值发起下一跳(相对路径,3.x 变更,需自行拼主机名):
curl -s -H "Authorization: Bearer $CT" 'http://127.0.0.1:8144/message?limit=2&since=4'
实测回显:
{"paging":{"next":"/message?limit=2&since=2","size":2,"since":2,"limit":2},"messages":[{"id":3,"appid":1,"message":"这条消息经 /stream 实时下发","title":"WebSocket 实时验证","priority":6,"date":"2026-10-21T19:59:59.449049731+08:00"},{"id":2,"appid":1,"message":"/var 分区使用率 92%","title":"磁盘告警","priority":8,"extras":{...},"date":"2026-10-21T19:59:16.428773726+08:00"}]}
无更多数据时 next 字段缺省。应用维度消息列表用 GET /application/1/message(应用令牌可查自己,客户端令牌亦可查),回显结构与全局列表一致。
8.3 删除闭环(DELETE /message/{id})
删除对应用令牌关闭、客户端令牌开放。先用应用令牌试删(预期 401):
curl -s -w '\nHTTP %{http_code}\n' -X DELETE "http://127.0.0.1:8144/message/8?token=$AT"
实测回显:
{"error":"Unauthorized","errorCode":401,"errorDescription":"you need to provide a valid access token or user credentials to access this api"}
HTTP 401
换客户端令牌:
curl -s -w '\nHTTP %{http_code}\n' -X DELETE "http://127.0.0.1:8144/message/8?token=$CT"
curl -s -w '\nHTTP %{http_code}\n' -X DELETE "http://127.0.0.1:8144/message/8?token=$CT"
实测回显(成功 + 重复删除):
HTTP 200
{"error":"Not Found","errorCode":404,"errorDescription":"message does not exist"}
HTTP 404
删除成功 200、已删消息再删 404(幂等语义靠 404 表达,脚本重试时按此处理)。清空某应用全部消息用 DELETE /application/{id}/message(同 client 令牌权限)。
九、user 域:提权后的用户管理
第六章的 low-priv-cli 提权后,创建用户成功:
curl -s -w '\nHTTP %{http_code}\n' -X POST -H "Authorization: Bearer $CT3" \
-H 'Content-Type: application/json' -d '{"name":"dev1","pass":"dev1pass"}' http://127.0.0.1:8144/user
实测回显:
{"id":2,"name":"dev1","admin":false,"createdAt":"2026-10-21T20:00:55.255101531+08:00"}
HTTP 200
列用户与再建一个:
curl -s -H "Authorization: Bearer $CT3" http://127.0.0.1:8144/user
实测回显:
[{"id":1,"name":"admin","admin":true,"createdAt":"2026-10-21T19:54:33.694487038+08:00"},{"id":2,"name":"dev1","admin":false,"createdAt":"2026-10-21T20:00:55.255101531+08:00"}]
{"id":3,"name":"dev2","admin":false,"createdAt":"2026-10-21T20:01:09.345169167+08:00"}
HTTP 200
用户管理闭环小结:普通会话 403 → elevate 204(+1h 窗口)→ POST/GET /user 200。改别人口令走 PUT /user/{id},删除走 DELETE /user/{id}(同权限要求);改自己口令用 PUT /current/user/password(无需提权)。
十、plugin 与实验性端点
10.1 GET /plugin
curl -s -H "Authorization: Bearer $CT" http://127.0.0.1:8144/plugin
实测回显:
[]
本实例未装载插件(配置篇目录参数 GOTIFY_PLUGINSDIR 为空目录),返回空数组。插件系统支持 Go 共享库形态的热插拔(enable/disable/config/display 四组端点见第三章总览)。
10.2 AI Agent (MCP) 端点:3.1.1 实测 404
官方文档新增「AI Agents (MCP)」指南,提及服务端暴露 /mcp 端点。3.1.1 稳定版二进制实测:
curl -s -w '\nHTTP %{http_code}\n' http://127.0.0.1:8144/mcp
实测回显:
{"error":"Not Found","errorCode":404,"errorDescription":"page not found"}
HTTP 404
该能力尚未进入 3.1.1 稳定版(面向 master 主线),生产评估时勿按稳定版默认可用对待,升级后再验证。
十一、WebSocket /stream 实测
GET /stream(客户端令牌)升级为 WebSocket 长连接,实时接收全部消息。验证脚本思路:建连 → 1 秒后用 HTTP 推一条 → 等待从流上收到同一帧 → 主动关闭。探针代码(依赖 pip install websocket-client):
#!/usr/bin/env python3
import json, threading, time, urllib.request
import websocket
BASE = "http://127.0.0.1:8144"
WS_URL = "ws://127.0.0.1:8144/stream"
CLI_TOKEN = "gtfyc.xxxx"
APP_TOKEN = "gtfya.xxxx"
def push_after(seconds):
def _run():
time.sleep(seconds)
body = json.dumps({"title": "WebSocket 实时验证",
"message": "这条消息经 /stream 实时下发",
"priority": 6}).encode()
req = urllib.request.Request(BASE + "/message?token=" + APP_TOKEN,
data=body,
headers={"Content-Type": "application/json"},
method="POST")
with urllib.request.urlopen(req) as resp:
print("[push] HTTP", resp.status, resp.read().decode())
threading.Thread(target=_run).start()
ws = websocket.create_connection(WS_URL + "?token=" + CLI_TOKEN, timeout=15)
print("[ws] connected:", ws.connected)
push_after(1)
try:
while True:
frame = ws.recv()
print("[ws] recv:", frame)
if "WebSocket 实时验证" in frame:
break
finally:
ws.close()
实测回显:
[ws] connected: True
[push] HTTP 200 {"id":3,"appid":1,"message":"这条消息经 /stream 实时下发","title":"WebSocket 实时验证","priority":6,"date":"2026-10-21T19:59:59.449049731+08:00"}
[ws] recv: {"id":3,"appid":1,"message":"这条消息经 /stream 实时下发","title":"WebSocket 实时验证","priority":6,"date":"2026-10-21T19:59:59.449049731+08:00"}
[ws] closed: True
HTTP 推送与 WebSocket 下发帧字段完全一致,端到端延迟在秒级内。连接保持期间服务端每 45 秒发 ping 帧(GOTIFY_SERVER_STREAM_PINGPERIODSECONDS,配置篇第三章),跨域接入需配 GOTIFY_SERVER_STREAM_ALLOWEDORIGINS(同源始终放行)。
十二、常见问题(FAQ)
12.1 令牌只有一次明文机会,丢了怎么办?
创建响应(POST /application、POST /client、登录 Set-Cookie)是明文唯一出现时刻,列表与查询接口不回 token。丢失后:应用走 PUT /application/{id}/security、客户端走 PUT /client/{id}/security 轮换(7.2 节闭环),轮换前旧令牌持续有效,泄露场景应立即轮换。
12.2 PUT /application/{id} 返回了一个 token,为什么用不了?
那是脱敏回显值(7.1 节实测陷阱):旧 token 仍 200、响应值 401。真轮换只认 security 端点。
12.3 用户管理接口为什么一直 403?
/user 域要求 Basic Auth 或提权会话(POST /client/{id}/elevate,需 admin 口令与 durationSeconds,默认窗口 1 小时,6.2 节闭环)。纯客户端令牌永远 403,这是设计行为而非故障。
12.4 推送 200 但收不到?
按链路排查:①令牌前缀对不对(应用 gtfya. 才能推);②消息是否进了库(GET /message 或 WebUI 查);③接收端是否在线(WebUI 依赖 /stream 长连接,Android 端受 priority 影响,priority=0 不弹通知,8.1 节语义表);④服务端 /health 与 journalctl。
12.5 paging.next 直接 curl 报错?
next 是相对路径(如 /message?limit=2&since=4),需拼接 http://host:port 前缀再请求(8.2 节)。
12.6 /mcp 端点 404 是坏了吗?
不是。MCP 端点未随 3.1.1 稳定版发布(10.2 节实测),官方文档面向 master 主线;升级包含该能力的版本后再验证。
12.7 Basic Auth 全局可用吗?
可用但建议最小化:Basic 等于完整管理员权限(还能绕过提权窗口),自动化脚本优先用客户端/应用令牌分域授权,Basic 只留给提权与应急操作。
十三、参考链接
- 官方 REST API 文档:https://gotify.net/docs/rest-api 与 https://gotify.net/api-docs (Swagger 在线版)
- 实例内建 Swagger UI:http://<服务器>:8144/swagger/ (3.1.1 OpenAPI 2.0 规范)
- 消息优先级:https://gotify.net/docs/priority
- 消息 extras:https://gotify.net/docs/messageextras (client::display / client::notification 等)
- 会话提权:https://gotify.net/docs/session-elevation
- 推送消息示例集:https://gotify.net/docs/push-message-examples (curl/脚本/各语言示例)
- 认知与选型:《Gotify 3.1.1 介绍与选型指南》
- 安装部署:《Ubuntu 22.04 安装 Gotify 3.1.1 指南》
- 配置参数:《Gotify 3.1.1 配置参数详解》