Beaver Habit Tracker v0.10.0 API 使用详解

BeaverHabits v0.10.0 REST API 与 WebSocket 实时同步详解:JWT 认证、14 个端点全量实测回显、打卡查询过滤约定、错误码表与对接常见问题。

Beaver Habit TrackerBeaver Habit Tracker习惯追踪REST API2026/10/23

Beaver Habit Tracker v0.10.0 API 使用详解

目标:完整梳理 Beaver Habit Tracker(BeaverHabits)v0.10.0 的 REST API 与 WebSocket 实时同步接口——认证方式、全部端点、参数、请求示例与实测响应,帮助你用脚本或第三方客户端(如移动端、Home Assistant、自动化平台)对接自己的习惯数据。本文全部请求均在 v0.10.0 实例上用 curl 逐条实测,响应为真实回显(长响应标注节选)。版本查询日期 2026-10-22。

一、环境信息

项目值
组件版本BeaverHabits v0.10.0(2026-07-30 发布,官方最新稳定版)
API 风格REST(JSON over HTTP),路由前缀 /api/v1;另含 1 个 WebSocket 端点
API 文档自带 Swagger UI:http://<服务器IP>:8080/docs(FastAPI 自动生成)
认证JWT Bearer(登录获取)或 API Token(页面内生成,v0.9.0+)
默认端口8080
实测环境Ubuntu 22.04 + 官方镜像 daya0576/beaverhabits:0.10.0 部署实例(部署见 《Beaver Habit Tracker v0.10.0 Docker Compose 部署指南》)
示例账号admin@example.com / q1w2e3r4

二、前置条件

  • 一个可访问的 BeaverHabits v0.10.0 实例(本机或远程),本文以部署在服务器本机的 http://127.0.0.1:8080 为基准地址(下文记作 BASE),远程调用把地址换成 http://<服务器IP>:8080;
  • 一个已注册账号(注册方法见 4.1 节);
  • 命令行工具 curl(或任何能发 HTTP 请求的客户端);
  • 如需查看交互式文档,浏览器打开 /docs:
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8080/docs

实测回显:

200

三、API 总览

3.1 端点一览

方法路径作用
POST/auth/register注册账号
POST/auth/login登录,返回 JWT
GET/users/me当前用户信息
GET/api/v1/habits习惯列表
POST/api/v1/habits创建习惯
GET/api/v1/habits/meta习惯排序元数据
PUT/api/v1/habits/meta修改习惯排序
GET/api/v1/habits/export导出全部习惯(含记录)
GET/api/v1/habits/{habit_id}习惯详情(含打卡记录)
PUT/api/v1/habits/{habit_id}更新习惯(名称/星标/状态/目标频率/标签)
DELETE/api/v1/habits/{habit_id}删除习惯
GET/api/v1/habits/{habit_id}/completions查询打卡日期
POST/api/v1/habits/{habit_id}/completions打卡/取消打卡
WS/api/v1/sync/wsWebSocket 实时同步(v0.10.0 新增)

3.2 协议与认证约定

  • 认证方式:除注册与登录外,全部端点要求 Authorization: Bearer <令牌> 请求头。令牌有两种:/auth/login 返回的 JWT(默认长期有效,见 JWT_LIFETIME_SECONDS),或在 Web 界面 Menu → Tools → API Tokens 生成的永久 API Token(v0.9.0+)。REST 端点走 JWT;WebSocket 端点两者都接受(源码先查 JWT 再查 API Token);
  • 数据格式:请求与响应均为 JSON(登录除外,登录是表单编码);
  • 日期约定:打卡接口默认日期格式 DD-MM-YYYY(可用 date_fmt 参数改);习惯详情与导出中的记录日期为 YYYY-MM-DD;
  • 习惯 ID:6 位短 ID(如 ead5ef),创建成功时返回;
  • 限流约定:服务端无内建限流;生产实例为 gunicorn 单 worker(WebSocket 粘性要求),脚本批量调用注意控制并发,避免占满唯一 worker。

四、认证与首次调用

4.1 注册账号(已有账号跳过)

curl -s -X POST http://127.0.0.1:8080/auth/register \
  -H "Content-Type: application/json" \
  -d '{"email":"admin@example.com","password":"q1w2e3r4"}'

实测回显:

{"id":"66d57e38-2585-4dd6-a9a3-f222d5d5a1a5","email":"admin@example.com","is_active":true,"is_superuser":false,"is_verified":false}

4.2 登录获取 JWT

登录接口是表单编码(application/x-www-form-urlencoded),grant_type 固定 password:

curl -s -X POST http://127.0.0.1:8080/auth/login \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=password&username=admin@example.com&password=q1w2e3r4"

实测回显(token 为长 JWT,此处节选):

{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI2NmQ1N2UzOC0 ...(JWT 节选)","token_type":"bearer"}

4.3 带 JWT 首次调用

把返回的 access_token 放进 Authorization: Bearer 头调用任意端点。下文示例统一先用变量存放令牌:

TOKEN=$(curl -s -X POST http://127.0.0.1:8080/auth/login \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=password&username=admin@example.com&password=q1w2e3r4" \
  | python3 -c "import sys,json;print(json.load(sys.stdin)['access_token'])")
curl -s http://127.0.0.1:8080/users/me -H "Authorization: Bearer $TOKEN"

实测回显:

{"id":"66d57e38-2585-4dd6-a9a3-f222d5d5a1a5","email":"admin@example.com","is_active":true,"is_superuser":false,"is_verified":false}

不带或带错令牌时返回 401:

curl -s http://127.0.0.1:8080/api/v1/habits -H "Authorization: Bearer invalid.token.here"

实测回显:

{"detail":"Not authenticated"}

五、各 API 域详解

5.1 习惯列表域

创建习惯 POST /api/v1/habits(参数:name,必填):

curl -s -X POST http://127.0.0.1:8080/api/v1/habits \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"name":"Drink Water"}'

实测回显:

{"id":"ead5ef","name":"Drink Water"}

习惯列表 GET /api/v1/habits(可选参数:status,默认 active,取值 active/archive/soft_delete):

curl -s http://127.0.0.1:8080/api/v1/habits -H "Authorization: Bearer $TOKEN"

实测回显:

[{"id":"ead5ef","name":"Drink Water"}]

排序元数据 GET /api/v1/habits/meta 与 PUT /api/v1/habits/meta(参数:order,习惯 ID 数组,决定页面显示顺序):

curl -s -X PUT http://127.0.0.1:8080/api/v1/habits/meta \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"order":["ead5ef"]}'
curl -s http://127.0.0.1:8080/api/v1/habits/meta -H "Authorization: Bearer $TOKEN"

实测回显(两次调用):

{"order":["ead5ef"]}
{"order":["ead5ef"]}

5.2 习惯详情域

详情 GET /api/v1/habits/{habit_id}(返回全部打卡记录):

curl -s http://127.0.0.1:8080/api/v1/habits/ead5ef -H "Authorization: Bearer $TOKEN"

实测回显(打卡两天后):

{"id":"ead5ef","name":"Drink Water","star":false,"records":[{"data":{"day":"2026-10-22","done":true,"timestamp":1792689300626}},{"data":{"day":"2026-10-21","done":true,"timestamp":1792689300641,"text":"5km"}}],"status":"active","period":null,"tags":[]}

记录(records)中每条 data 含 day(YYYY-MM-DD)、done、timestamp(毫秒),可选 text 为打卡备注。

更新习惯 PUT /api/v1/habits/{habit_id}(可更新字段:name、star、status、period、tags,均可选传):

curl -s -X PUT http://127.0.0.1:8080/api/v1/habits/ead5ef \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"star":true}'

实测回显:

{"id":"ead5ef","name":"Drink Water","star":true,"records":[{"data":{"day":"2026-10-22","done":true,"timestamp":1792689300626}},{"data":{"day":"2026-10-21","done":true,"timestamp":1792689300641,"text":"5km"}}],"status":"active","period":null,"tags":[]}

period(目标频率)结构为 {"period_type":"D|W|M|Y","period_count":N,"target_count":M},例如「每周至少 3 次」即 period_type=W, period_count=1, target_count=3。status 可传 archive 归档习惯。

删除习惯 DELETE /api/v1/habits/{habit_id}(返回被删习惯对象;⚠️ 删除即生效,记录一并删除):

curl -s -X DELETE http://127.0.0.1:8080/api/v1/habits/65c250 -H "Authorization: Bearer $TOKEN"

实测回显(删除一条名为 Temp Del 的测试习惯):

{"id":"65c250","name":"Temp Del","star":false,"records":[],"status":"active","period":null,"tags":[]}

5.3 打卡记录域

打卡/取消打卡 POST /api/v1/habits/{habit_id}/completions。参数:done(布尔,必填)、date(字符串,必填,默认格式 DD-MM-YYYY)、text(备注,可选)、date_fmt(自定义日期格式,可选):

curl -s -X POST http://127.0.0.1:8080/api/v1/habits/ead5ef/completions \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"done":true,"date":"22-10-2026"}'

实测回显:

{"day":"22-10-2026","done":true}

带备注打卡(另一天):

curl -s -X POST http://127.0.0.1:8080/api/v1/habits/ead5ef/completions \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{"done":true,"date":"21-10-2026","text":"5km"}'

实测回显:

{"day":"21-10-2026","done":true}

取消打卡传 "done":false(会追加一条 done=false 记录,覆盖当天完成状态)。

查询打卡日期 GET /api/v1/habits/{habit_id}/completions(返回已打卡日期数组):

curl -s "http://127.0.0.1:8080/api/v1/habits/ead5ef/completions" -H "Authorization: Bearer $TOKEN"

实测回显:

["21-10-2026","22-10-2026"]

过滤与排序(status/date_start/date_end/limit/sort/date_fmt,详见第六节):

curl -s "http://127.0.0.1:8080/api/v1/habits/ead5ef/completions?sort=desc&limit=1" -H "Authorization: Bearer $TOKEN"

实测回显:

["22-10-2026"]

5.4 数据导出域

GET /api/v1/habits/export 导出当前账号全部习惯与记录(原始字典透传,客户端字段无损往返),可用于备份或迁移到其他存储形态:

curl -s http://127.0.0.1:8080/api/v1/habits/export -H "Authorization: Bearer $TOKEN"

实测回显:

{"habits":[{"name":"Drink Water","records":[{"day":"2026-10-22","done":true,"timestamp":1792689300626},{"day":"2026-10-21","done":true,"timestamp":1792689300641,"text":"5km"}],"id":"ead5ef","tags":[],"star":true}],"order":["ead5ef"]}

⚠️ v0.10.0 没有 REST 导入端点;Web 界面的导入功能(设置页 Import)会把该格式的 JSON 合并回服务端,可用于跨存储迁移。

5.5 实时同步 WebSocket

/api/v1/sync/ws 是 v0.10.0 新增的 WebSocket 端点,用于多端实时同步打卡:客户端连上后发送 push_tick(打卡)或 push_habit_list(整表同步)消息,服务端持久化后回 *_ack 确认,并把变更广播给该账号的其他在线连接。

  • 连接地址:ws://<服务器IP>:8080/api/v1/sync/ws?token=<JWT 或 API Token>(认证走查询参数);
  • push_tick 消息字段:type、request_id(客户端生成,用于 ack 对应)、habit_id、day(格式固定 YYYY-MM-DD)、done、text(可选);
  • 无效/缺失令牌时握手直接被拒绝(服务端以 1008 policy violation 关闭),客户端表现为连接升级失败。

实测(在实例上用 Python websockets 库连接并发送一次打卡):

import asyncio, json, urllib.request, urllib.parse
data = urllib.parse.urlencode({"grant_type":"password","username":"admin@example.com","password":"q1w2e3r4"}).encode()
req = urllib.request.Request("http://127.0.0.1:8080/auth/login", data=data, headers={"Content-Type":"application/x-www-form-urlencoded"})
token = json.load(urllib.request.urlopen(req))["access_token"]

import websockets
async def main():
    uri = "ws://127.0.0.1:8080/api/v1/sync/ws?token=" + token
    async with websockets.connect(uri) as ws:
        await ws.send(json.dumps({"type":"push_tick","request_id":"r1","habit_id":"ead5ef","day":"2026-10-20","done":True}))
        print("recv:", await ws.recv())
asyncio.run(main())

实测回显:

recv: {"type":"tick_ack","request_id":"r1","timestamp":1792689346203}

无令牌连接的实测结果:

closed: InvalidStatus None

六、分页/过滤/排序通用约定

BeaverHabits 的 REST API 没有分页:习惯列表一次返回全部;打卡日期查询按时间过滤后最多返回 limit 条(默认 10)。打卡查询(GET /api/v1/habits/{habit_id}/completions)支持的通用参数如下(全部实测有效):

参数默认值说明
status只返回完成(DONE)完成状态过滤,多个用逗号分隔(如 DONE,SKIP),非法值 400
date_fmt%d-%m-%Y输入输出日期格式(strptime/strftime 格式串,如 %Y-%m-%d)
date_start / date_end不限起止日期(按 date_fmt 解析),start > end 时 400
limit10返回条数上限,0 或空返回全部
sortascasc 升序 / desc 降序,其他值 400

习惯列表的 status 参数(GET /api/v1/habits)与之不同:过滤的是习惯本身的状态(active/archive/soft_delete)。

七、常见问题

7.1 401 Not authenticated

令牌缺失、写错(没有 Bearer 前缀)或 JWT 已过期(设置了 JWT_LIFETIME_SECONDS 后)。重新登录获取令牌;脚本里建议把 4.3 节的取令牌逻辑封装成函数。

7.2 404 "The habit list data may be broken or missing"

新注册账号(还没有任何习惯清单文件)调用 GET /api/v1/habits 会返回该 404(实测见上),先 POST /api/v1/habits 创建第一个习惯即可。切换过 HABITS_STORAGE 的账号出现同样报错,说明当前存储下没有该账号的数据,处理见配置详解篇 7.4 节。

7.3 400 Invalid date format / Invalid sort value

打卡接口默认只认 DD-MM-YYYY(日-月-年)顺序,2026-10-22 会被判非法,需传 22-10-2026 或显式指定 date_fmt(如 %Y-%m-%d)。sort 只接受 asc/desc。

7.4 REST 端点能用 API Token 吗

REST 端点只认 JWT;API Token(页面 Menu → Tools → API Tokens 生成)目前用于 WebSocket 端点(?token= 同时接受 JWT 与 API Token)及配套的客户端生态。脚本对接 REST 建议用登录 JWT。

7.5 批量调用把服务打满了

生产实例是 gunicorn 单 worker(WebSocket 同步依赖单进程广播),大量并发请求会排队。脚本批量打卡请在请求间加间隔,或降低并发数。

7.6 想看在线文档但 /docs 打不开

/docs 由 FastAPI 自动挂载,v0.10.0 始终可用(实测 200)。若无法访问,确认地址端口与实例部署一致(容器部署映射的宿主端口可能不是 8080),且未被反向代理规则拦截。

八、参考链接