Donetick 0.1.83 API 使用详解

详解 Donetick v0.1.83 双 API 体系:JWT 与 dt_ 长令牌认证、任务/标签/用户/实时推送/eAPI 各域接口实测示例与响应回显、错误码处理与外部集成选型,全部请求在 Ubuntu 22.04 自托管实例实测采集。

Donetick任务管理自托管2026/10/23

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 认证方式

三种凭证(均实测):

  1. JWT 双令牌:POST /api/v1/auth/login 返回 access_token(默认 7 天)与 refresh_token(默认 60 天),请求头 Authorization: Bearer <access_token>;
  2. API Token:POST /api/v1/users/tokens 创建,dt_ 前缀长令牌,仅供 /eapi/v1 使用,请求头 secretkey: <token>,不设过期;
  3. 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/:idGET查询单个 Thing
/eapi/v1/things/:id/stateGET查询当前状态
/eapi/v1/things/:id/state/changeGET上报状态变化(触发联动规则)

调用方式与 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。两者数据同源,任务对象结构一致。

十、参考链接

  1. 实例自带 OpenAPI 描述:http://<服务器IP>:2021/swagger/doc.json
  2. 官方仓库(API 源码入口 internal/):https://github.com/donetick/donetick
  3. 本专栏部署篇:《Ubuntu 22.04 安装 Donetick 0.1.83 指南》
  4. 本专栏配置篇:《Donetick 0.1.83 配置参数详解》
  5. 本专栏认知篇:《Donetick 0.1.83 介绍与选型指南》