Donetick 0.1.83 API 使用详解
详解 Donetick v0.1.83 双 API 体系:JWT 与 dt_ 长令牌认证、任务/标签/用户/实时推送/eAPI 各域接口实测示例与响应回显、错误码处理与外部集成选型,全部请求在 Ubuntu 22.04 自托管实例实测采集。
Donetick 0.1.83 API 使用详解
目标:系统讲解 Donetick v0.1.83 的 REST API——JWT 与
dt_长令牌双认证体系、任务/标签/用户/实时推送/eAPI 各域接口的请求示例与真实响应回显、错误码与外部集成方法。全部请求均在 Ubuntu 22.04 自托管实例(v0.1.83)实测采集。
一、环境信息
- 实例环境:Ubuntu 22.04.5 LTS x86_64 + Donetick v0.1.83(自托管形态)
- 服务端点:
http://localhost:2021(下文示例统一用此地址,远程服务器替换为实际 IP/域名) - API 文档:实例自带 Swagger UI(
serve_swagger: true时):/swagger/index.html,OpenAPI 描述文件/swagger/doc.json(实测返回application/json) - 认证体系:
/api/v1用 JWT(登录换 token);/eapi/v1用dt_前缀长令牌(API Token) - 测试账号:
wangli/ 演示口令q1w2e3r4(演示环境测试值) - 安装与配置见《Ubuntu 22.04 安装 Donetick 0.1.83 指南》与《Donetick 0.1.83 配置参数详解》
二、前置条件
2.1 服务与端点确认
确认服务可用且 API 健康(无需认证):
curl -s -w ' [%{http_code}]' http://localhost:2021/api/v1/health
实测输出:
[200]
2.2 客户端工具
本文全部示例用 curl(Ubuntu 22.04 自带)。响应为 JSON,可按需接 jq 美化;本文为保证「原样回显」不加管道处理。
2.3 账号准备
API 调用需要一个已注册账号。注册接口 POST /api/v1/auth/(见 4.1),或经 Web 界面 http://<服务器IP>:2021 注册。注册用户自动归属一个 Circle(圆圈),任务等资源挂在 Circle 下隔离。
三、API 总览
3.1 双 API 体系
v0.1.83 对外暴露两套 REST API:
| 体系 | 前缀 | 认证 | 面向对象 | 典型用途 |
|---|---|---|---|---|
| 应用 API | /api/v1/ | JWT(Bearer) | Web 端/移动端/脚本 | 全功能:任务、标签、用户、实时、文件 |
| 外部集成 API(eAPI) | /eapi/v1/ | dt_ 长令牌(secretkey 头) | 第三方系统、Home Assistant 类集成 | 任务查询/创建/删除、Things 状态联动 |
本文实测覆盖:健康检查、认证域(注册/登录/刷新/登出)、任务域(列表/详情/创建/完成/历史/优先级/归档/删除)、标签域、筛选域、用户域(资料/令牌/存储)、实时域(SSE)、eAPI 全部已注册路由。完整路由清单可随时用 /swagger/doc.json 拉取核对。
3.2 认证方式
三种凭证(均实测):
- JWT 双令牌:
POST /api/v1/auth/login返回access_token(默认 7 天)与refresh_token(默认 60 天),请求头Authorization: Bearer <access_token>; - API Token:
POST /api/v1/users/tokens创建,dt_前缀长令牌,仅供/eapi/v1使用,请求头secretkey: <token>,不设过期; - SSE Ticket:
GET /api/v1/realtime/sse/ticket换取 60 秒一次性票据(实测expiresIn: 60),供原生客户端建立事件流连接。
3.3 通用约定
- 数据格式:请求/响应均 JSON;创建类接口成功返回 200/201 +
{"res":...}或{"message":...}; - 错误结构:
{"error":"<原因>"}+ 对应 HTTP 状态码(401 未认证 / 403 禁止 / 400 参数非法); - 限流:全局 60 秒 300 次(
server.rate_limit/rate_period),登录注册等敏感接口另有独立限流; - 响应体积大时本文标注「节选」;JWT 与 API Token 等敏感值一律掩码展示。
四、认证与首次调用(实测)
4.1 注册首个账号
curl -s -w '\n[%{http_code}]\n' -X POST http://localhost:2021/api/v1/auth/ \
-H 'Content-Type: application/json' \
-d '{"username":"wangli","password":"q1w2e3r4","email":"wangli@example.com","displayName":"王丽"}'
实测输出:
{}
[201]
参数约束:username 4–20 字符、password 8–64 字符、email 必填且格式合法、displayName 可选(缺省取 username)。实例关闭注册(is_user_creation_disabled: true)时返回 403 {"error":"User creation is disabled"}。
4.2 登录获取 JWT
curl -s -X POST http://localhost:2021/api/v1/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"wangli","password":"q1w2e3r4"}'
实测输出(令牌串掩码,字段完整保留):
{"access_token":"<JWT_掩码>","refresh_token":"<refresh_掩码>","access_token_expiry":"2026-10-30T02:28:24.008049364Z","refresh_token_expiry":"2026-12-22T02:28:24.008049802Z","token_type":"Bearer","token":"<JWT_掩码>","expire":"2026-10-30T10:28:24.002063157+08:00"}
错误口令实测:
curl -s -w ' [%{http_code}]' -X POST http://localhost:2021/api/v1/auth/login \
-H 'Content-Type: application/json' -d '{"username":"wangli","password":"wrong-pass-123"}'
{"error":"Invalid credentials"} [401]
有效期与配置的对应关系实测:登录时刻 2026-10-23,access_token_expiry 为 7 天后(jwt.session_time: 168h)、refresh_token_expiry 为 60 天后(jwt.max_refresh: 1440h)。
后续示例统一携带请求头($TOKEN 为上一步取到的 access_token):
A="Authorization: Bearer $TOKEN"
4.3 刷新令牌
access_token 过期前用 refresh_token 静默续期:
curl -s -X POST http://localhost:2021/api/v1/auth/refresh \
-H 'Content-Type: application/json' \
-d '{"refresh_token":"<上一步的refresh_token>"}'
实测输出(节选,令牌掩码):
{"access_token":"<JWT_掩码>","refresh_token":"<refresh_掩码>","access_token_expiry":"2026-10-30T02:46:52.229488483Z","refresh_token_expiry":"2026-12-22T02:46:52.229488544Z","token_type":"Bearer","token":"<JWT_掩码>","expire":"2026-10-30T10:46:52.2294686+08:00"}
换发后两个令牌都更新,过期时间重新起算。
4.4 登出
curl -s -w ' [%{http_code}]' -X POST http://localhost:2021/api/v1/auth/logout \
-H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-d '{"refresh_token":"<当前refresh_token>"}'
实测输出:
{"message":"Logged out successfully"} [200]
登出会吊销该 refresh_token 并清理会话。
五、任务域 API(/api/v1/chores)
5.1 查询任务列表与详情
列表返回当前用户 Circle 下的全部任务(无分页参数,一次全量):
curl -s http://localhost:2021/api/v1/chores/ -H "$A"
实测输出(节选,本例 Circle 中有 3 条任务):
{"res":[{"completionActions":null,"id":2,"name":"快递代取","frequencyType":"weekly","frequency":1,"frequencyMetadata":null,"nextDueDate":null,"isRolling":false,"assignedTo":null,"assignees":[],"assignStrategy":"no_assignee","isActive":true,"notification":false,"notificationMetadata":null,"labelsV2":[],"circleId":2,"createdAt":"2026-10-23T02:31:35.495974798Z","updatedAt":"2026-10-23T10:31:35.498284473+08:00","createdBy":2,"updatedBy":2,"thingChore":null,"status":0,"priority":0,"requireApproval":false,"isPrivate":false,"syncVersion":1}, ...]}
按 ID 查详情:
curl -s http://localhost:2021/api/v1/chores/4 -H "$A"
实测输出:
{"res":{"completionActions":null,"id":4,"name":"换滤芯","frequencyType":"once","frequency":1,"frequencyMetadata":null,"nextDueDate":null,"isRolling":false,"assignedTo":null,"assignees":[],"assignStrategy":"no_assignee","isActive":true,"notification":false,"notificationMetadata":null,"labelsV2":[],"circleId":2,"createdAt":"2026-10-23T02:46:29.319676796Z","updatedAt":"2026-10-23T10:46:29.319874076+08:00","createdBy":2,"updatedBy":2,"thingChore":null,"status":0,"priority":2,"subTasks":[],"requireApproval":false,"isPrivate":false,"syncVersion":3}}
任务对象关键字段解读:frequencyType 调度类型(once/daily/weekly/monthly/yearly/days_of_week 等);nextDueDate 下次到期;assignStrategy 指派策略(no_assignee/random/round_robin/least_assigned);priority 0–4;syncVersion 乐观同步版本号(实时推送用);isActive 是否启用(once 任务完成后转 false)。
5.2 创建任务
每周循环任务:
curl -s -X POST http://localhost:2021/api/v1/chores/ \
-H "$A" -H 'Content-Type: application/json' \
-d '{"name":"倒垃圾","frequencyType":"weekly","frequency":1,"isActive":true,"assignStrategy":"no_assignee"}'
实测输出:
{"res":3,"warnings":["priority not provided, defaulting to 0","isPrivate not provided, defaulting to false"]}
res 为新任务 ID;warnings 列出服务端补默认值的字段——这是 v0.1.83 的显性默认值机制,未传字段不用担心,但要看清补了什么。
一次性任务(带到期时间与优先级):
curl -s -X POST http://localhost:2021/api/v1/chores/ \
-H "$A" -H 'Content-Type: application/json' \
-d '{"name":"换滤芯","frequencyType":"once","dueDate":"2026-11-05T09:00:00+08:00","priority":2,"assignStrategy":"no_assignee"}'
实测输出:
{"res":4,"warnings":["frequency not provided, defaulting to 1","isActive not provided, defaulting to true","isPrivate not provided, defaulting to false"]}
5.3 完成任务与历史
完成任务(可附备注):
curl -s -X POST http://localhost:2021/api/v1/chores/4/do \
-H "$A" -H 'Content-Type: application/json' -d '{"notes":"已换新"}'
实测输出(节选,注意 isActive 已转 false——once 任务完成后即停用,不再排期):
{"res":{"completionActions":null,"id":4,"name":"换滤芯","frequencyType":"once","frequency":1,"frequencyMetadata":null,"nextDueDate":null,"isRolling":false,"assignedTo":null,"assignees":[],"assignStrategy":"no_assignee","isActive":false,...,"syncVersion":4}}
查询完成历史:
curl -s http://localhost:2021/api/v1/chores/4/history -H "$A"
实测输出:
{"res":[{"id":1,"choreId":4,"performedAt":"2026-10-23T02:46:29.351297309Z","completedBy":2,"assignedTo":null,"notes":"已换新","dueDate":null,"updatedAt":"2026-10-23T10:46:29.355933263+08:00","createdAt":"2026-10-23T10:46:29.355933263+08:00","status":1,"syncVersion":4}]}
💡 说明:循环任务(如
weekly)完成时响应里的nextDueDate会滚动到下一个周期(实测曾返回2026-10-29T18:35:33Z,恰为创建日 10-22 后的 7 天),isActive保持true;once任务完成即停用。这一差异是集成脚本判断「任务是否还需要跟进」的关键依据。
5.4 优先级、归档与删除
改优先级(0–4):
curl -s -X PUT http://localhost:2021/api/v1/chores/4/priority \
-H "$A" -H 'Content-Type: application/json' -d '{"priority":3}'
实测输出:
{"message":"Priority updated successfully"}
归档与恢复:
curl -s -X PUT http://localhost:2021/api/v1/chores/3/archive -H "$A"
curl -s -X PUT http://localhost:2021/api/v1/chores/3/unarchive -H "$A"
实测输出(依次):
{"message":"Chore archived successfully"}
{"message":"Chore unarchived successfully"}
删除(真删除,不可逆):
curl -s -w ' [%{http_code}]' -X DELETE http://localhost:2021/api/v1/chores/5 -H "$A"
实测输出:
{"message":"Chore deleted successfully","syncVersion":11} [200]
对已删除任务重复删除的实测报错(集成脚本可据此容错):
{"error":"Failed to retrieve chore"} [500]
六、标签与筛选域
6.1 标签(/api/v1/labels)
创建标签:
curl -s -X POST http://localhost:2021/api/v1/labels \
-H "$A" -H 'Content-Type: application/json' -d '{"name":"家务","color":"#FF5733"}'
实测输出:
{"res":{"id":1,"name":"家务","color":"#FF5733","created_by":2}}
列出标签:
curl -s http://localhost:2021/api/v1/labels -H "$A"
实测输出:
[{"id":1,"name":"家务","color":"#FF5733","created_by":2}]
标签经创建/更新任务的 labelsV2 字段挂到任务上。
6.2 筛选(/api/v1/filters)
筛选是保存起来的查询条件(Web 端「自定义筛选」),实例全新时为空:
curl -s http://localhost:2021/api/v1/filters -H "$A"
实测输出:
[]
七、用户与实时域
7.1 个人资料与存储用量
curl -s http://localhost:2021/api/v1/users/profile -H "$A"
实测输出:
{"res":{"id":2,"displayName":"王丽","username":"wangli","email":"wangli@example.com","provider":0,"circleID":2,"chatID":0,"image":"","timezone":"","userType":0,"mfaEnabled":false,"created_at":"2026-10-23T02:28:23.915818379Z","updated_at":"2026-10-23T10:28:23.942318235+08:00","disabled":false,"CustomerID":null,"subscription":"active","expiration":"2999-12-31T00:00:00Z","notification_target":{"userId":0,"type":0,"target_id":""},"webhookURL":null}}
circleID 是用户所属圆圈;自托管实例 subscription 恒为 active、expiration 为 2999 年(这也是 eAPI 高级接口全放行的原因,见第八章)。
存储用量(配额来自 storage.max_user_storage):
curl -s http://localhost:2021/api/v1/users/storage -H "$A"
实测输出(全新实例未传附件):
{"res":{"total":0,"used":0}}
7.2 API Token 管理(/api/v1/users/tokens)
创建长令牌:
curl -s -X POST http://localhost:2021/api/v1/users/tokens \
-H "$A" -H 'Content-Type: application/json' -d '{"name":"docs-demo"}'
实测输出(完整令牌仅创建响应返回一次,本文掩码):
{"res":{"id":1,"name":"docs-demo","userId":2,"token":"dt_********(32 位十六进制)","prefix":"dt_","createdAt":"2026-10-23T02:46:52.058168512Z"}}
列出令牌(服务端只回掩码形态,实测):
curl -s http://localhost:2021/api/v1/users/tokens -H "$A"
实测输出:
{"res":[{"id":1,"name":"docs-demo","userId":2,"maskedToken":"dt_********c2b2","createdAt":"2026-10-23T02:46:52.058168512Z"},{"id":2,"name":"docs-demo-2","userId":2,"maskedToken":"dt_********88a5","createdAt":"2026-10-23T02:46:52.072583187Z"}]}
7.3 实时推送(SSE)
先取一次性票据(60 秒有效、单次使用):
curl -s http://localhost:2021/api/v1/realtime/sse/ticket -H "$A"
实测输出(票据值截断展示):
{"expiresIn":60,"ticket":"ed03466dbf4f2a6f759856b32a71d9a18209c69dcbaad2c0945a8363d3d654b9"}
用 JWT 直连事件流(curl -N 不缓冲,3 秒采样):
curl -s -N --max-time 3 -H "Authorization: Bearer $TOKEN" http://localhost:2021/api/v1/realtime/sse
实测输出(响应头 + 首事件节选):
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
X-Accel-Buffering: no
Transfer-Encoding: chunked
data: {"type":"connection.established","timestamp":...}
事件为标准 SSE 帧(data: 行 + JSON),任务增删改都会推事件到同 Circle 的连接。反向代理时必须关闭缓冲(Nginx proxy_buffering off 或依赖响应里的 X-Accel-Buffering: no),否则收不到实时事件。
八、eAPI:外部集成专用 API(dt_ 令牌)
8.1 认证与错误码
eAPI 只认 dt_ 长令牌(7.2 创建),放请求头 secretkey。三种认证态实测:
无凭证:
curl -s -w ' [%{http_code}]' http://localhost:2021/eapi/v1/chore
{"error":"API token required"} [401]
令牌无效:
curl -s -w ' [%{http_code}]' -H 'secretkey: dt_ffffffffffffffffffffffffffffffff' http://localhost:2021/eapi/v1/chore
{"error":"Invalid API token"} [401]
有效令牌即放行(下一节)。eAPI 路由全集(源码核对):GET/POST /eapi/v1/chore、DELETE /eapi/v1/chore/:id、POST /eapi/v1/chore/:id/complete、PUT /eapi/v1/chore/:id、GET /eapi/v1/circle/members、/eapi/v1/things(GET /、GET /:id、GET /:id/state、GET /:id/state/change)。
💡 说明:
complete/update/circle/members在代码里标注「Plus member only」,但自托管实例用户expiration为 2999 年(见 7.1),IsPlusMember恒真,实测全部 200 可用。
8.2 任务 CRUD 实测
以下 $DTOK 为 7.2 创建的令牌(secretkey: $DTOK)。
列表(等同应用 API 的 /api/v1/chores/,另支持 ?includeSubtasks=true 带出子任务):
curl -s -H "secretkey: $DTOK" http://localhost:2021/eapi/v1/chore
实测输出(节选,与应用 API 返回同一批任务对象):
[{"completionActions":null,"id":2,"name":"快递代取","frequencyType":"weekly","frequency":1,...,"syncVersion":1},{"completionActions":null,"id":3,"name":"倒垃圾",...}]
创建(指定指派人 assignedTo 与策略):
curl -s -X POST http://localhost:2021/eapi/v1/chore \
-H "secretkey: $DTOK" -H 'Content-Type: application/json' \
-d '{"name":"浇花","frequencyType":"once","assignedTo":2,"assignStrategy":"no_assignee"}'
实测输出(节选,新任务 id=5):
{"completionActions":null,"id":5,"name":"浇花","frequencyType":"once","frequency":0,"frequencyMetadata":null,"nextDueDate":null,"isRolling":false,"assignedTo":2,"assignees":[{"userId":2}],"assignStrategy":"random","isActive":true,...}
完成:
curl -s -w ' [%{http_code}]' -X POST http://localhost:2021/eapi/v1/chore/5/complete \
-H "secretkey: $DTOK" -H 'Content-Type: application/json' -d '{}'
实测输出(节选,isActive 转 false):
{"completionActions":null,"id":5,"name":"浇花","frequencyType":"once",...,"isActive":false,...} [200]
更新(改名 + 改到期时间):
curl -s -w ' [%{http_code}]' -X PUT http://localhost:2021/eapi/v1/chore/5 \
-H "secretkey: $DTOK" -H 'Content-Type: application/json' \
-d '{"name":"浇花改","dueDate":"2026-11-01T09:00:00+08:00"}'
实测输出(节选,nextDueDate 已更新):
{"completionActions":null,"id":5,"name":"浇花改","frequencyType":"once",...,"nextDueDate":"2026-11-01T09:00:00+08:00",...} [200]
删除:
curl -s -X DELETE http://localhost:2021/eapi/v1/chore/5 -H "secretkey: $DTOK"
圆圈成员(含积分):
curl -s -H "secretkey: $DTOK" http://localhost:2021/eapi/v1/circle/members
实测输出:
[{"id":2,"userId":2,"circleId":2,"role":"admin","isActive":true,"createdAt":"2026-10-23T02:28:23.939186189Z","updatedAt":"2026-10-23T02:28:23.939186221Z","points":0,"pointsRedeemed":0,"username":"wangli","displayName":"王丽","image":""}]
8.3 Things 状态接口
/eapi/v1/things 用于外部系统回写「Things」状态(Donetick 可配置「某 Thing 变为某值时自动完成任务」,是智能家居联动的入口):
| 端点 | 方法 | 用途 |
|---|---|---|
/eapi/v1/things/ | GET | 列出全部 Things |
/eapi/v1/things/:id | GET | 查询单个 Thing |
/eapi/v1/things/:id/state | GET | 查询当前状态 |
/eapi/v1/things/:id/state/change | GET | 上报状态变化(触发联动规则) |
调用方式与 8.2 相同(secretkey 头),Things 需先在 Web 端创建并取得 ID。状态变化上报后,符合联动条件的任务由后台作业自动完成。
九、常见问题(FAQ)
Q1:所有接口都返回 401 Authentication required. Provide either a valid JWT token or API key.?
请求头没带对:应用 API 用 Authorization: Bearer <access_token>(注意 Bearer 后有一个空格);eAPI 用 secretkey: <dt_令牌>。两套凭证不可混用——dt_ 令牌打 /api/v1 无效。
Q2:access_token 过期(401)怎么办?
用 refresh_token 调 POST /api/v1/auth/refresh 换新(见 4.3);refresh_token 也过期(默认 60 天)则重新登录。集成脚本建议「请求前检查 access_token_expiry,过期先 refresh 再调用」。
Q3:创建任务成功但列表里看不到?
任务归属创建者的 Circle。确认查询与创建用的是同一个账号(/api/v1/users/profile 的 circleID 一致);跨 Circle 的任务互不可见,这是多家庭隔离的设计而非缺陷。
Q4:once 任务完成后接口返回里 nextDueDate 还是空?
正常。once 任务完成即 isActive: false,不再排期(实测见 5.3);需要重复的任务用 weekly/monthly 等类型,完成时 nextDueDate 自动滚动。
Q5:删除接口对不存在的 ID 返回 500 而非 404?
实测 v0.1.83 对重复删除返回 {"error":"Failed to retrieve chore"} [500]。集成脚本以「消息体是否含 Chore deleted successfully」判断成功,勿只看状态码。
Q6:SSE 连上但没有事件推送?
三步排查:① 反代是否缓冲(见 7.3 的 X-Accel-Buffering: no);② 连接用户与操作用户是否同 Circle(事件按 Circle 广播);③ 客户端是否用了票据模式——票据 60 秒过期且单次使用,过期重取。
Q7:想给 Home Assistant / 自研脚本对接,选哪套 API?
长驻集成选 eAPI(dt_ 令牌不过期、secretkey 头简单、Things 状态联动是它独有);需要实时事件流或全功能(标签/筛选/文件)则用 JWT 应用 API。两者数据同源,任务对象结构一致。
十、参考链接
- 实例自带 OpenAPI 描述:
http://<服务器IP>:2021/swagger/doc.json - 官方仓库(API 源码入口
internal/):https://github.com/donetick/donetick - 本专栏部署篇:《Ubuntu 22.04 安装 Donetick 0.1.83 指南》
- 本专栏配置篇:《Donetick 0.1.83 配置参数详解》
- 本专栏认知篇:《Donetick 0.1.83 介绍与选型指南》