一、适用场景
客户端 API 用于在局域网内把记乎桌面版接入自动化工具、脚本、内部系统或自定义制卡流程。外部程序可以通过 HTTP 管理当前桌面版中的牌组、目录、模板、卡片和资源。
这是桌面版的本地接口,不是记乎云端开放 API。接口实际操作当前电脑上的本地数据;需要将数据同步到云端时,请显式调用同步接口。
客户端 API 当前仅在 Preview 或开发构建中显示,并要求有效的 PLUS 会员。正式版没有设置入口。
二、启用方式
- 打开记乎桌面版设置。
- 进入“客户端 API”。
- 开启“启用客户端 API”。该功能需要 PLUS 会员。
- 复制页面显示的 API 地址、访问秘钥,或直接复制 OpenAPI 地址。
关闭开关后,API 服务会停止监听;重置访问秘钥后,旧秘钥立即失效。桌面版退出时也会停止 API 服务。
三、连接与鉴权
设置页显示的地址是完整的局域网访问地址,例如:
http://192.168.1.8:18080
所有需要鉴权的请求可选择以下任一种方式传递秘钥。建议使用请求头,避免秘钥出现在代理或访问日志中:
X-API-SECRET: <访问秘钥>
或
GET /decks/all?secret=<访问秘钥>
| 接口 | 访问秘钥 | 桌面版登录状态 |
|---|---|---|
GET /_check | 不需要 | 不需要 |
GET /openapi.json | 不需要 | 不需要 |
GET /app/info | 需要 | 不需要 |
| 其余接口 | 需要 | 需要已登录的桌面版会话 |
/_check 只用于判断 HTTP 服务是否存活,不代表桌面版已登录或牌组可用。/openapi.json 返回当前版本的机器可读接口描述。
四、通用约定
- JSON 请求使用
Content-Type: application/json;资源上传使用Content-Type: application/octet-stream。 - JSON 响应中的嵌套字段统一使用 snake_case,例如
deck_id、category_sn、created_at。卡片兼容字段_usn、_synced保留原名称。 - 返回实体的接口返回 JSON;删除和同步等无实体返回的成功响应为
text/plain的ok。 - 路径中的
deckId为正整数;categorySN、templateSN、cardSN和资源名称包含特殊字符时必须进行 URL 编码。 - JSON 请求正文上限为 42 MB,资源上传正文上限为 100 MB。超过上限返回
413 PAYLOAD_TOO_LARGE。
五、系统接口
| 方法和路径 | 说明 | 成功响应 |
|---|---|---|
GET /_check | 检查服务是否正在监听 | 文本 ok |
GET /openapi.json | 获取 OpenAPI 3.1 接口描述 | OpenAPI JSON |
GET /app/info | 获取桌面版和操作系统信息 | 应用信息对象 |
POST /sync | 同步全部本地牌组 | 文本 ok |
/app/info 返回字段包括 app_type、app_version、app_build_number、os_type、os_version 和 os_arch。该接口只需要 API 秘钥,不要求登录。
六、牌组接口
| 方法和路径 | 请求参数 | 返回 |
|---|---|---|
GET /decks/cloud | 无 | 云端牌组数组 |
GET /decks/all | 无 | 本地牌组数组 |
POST /decks/create | JSON:name、type、thumb | 新建牌组 |
GET /decks/{deckId}/info | 路径:deckId | 牌组对象 |
POST /decks/{deckId}/fetch | 路径:deckId | 下载后的牌组结果 |
POST /decks/{deckId}/update | JSON:name、thumb | 更新后的牌组 |
POST /decks/{deckId}/delete | 可选查询参数 completelyDelete=1 | 文本 ok |
POST /decks/{deckId}/sync | 路径:deckId | 文本 ok |
创建牌组示例:
POST /decks/create
Content-Type: application/json
X-API-SECRET: <访问秘钥>
{
"name": "英语词汇",
"type": 2,
"thumb": null
}
type 兼容旧客户端编号:0 普通牌组、1 笔记本、2 词汇、6 混合、7 题库。未填写时按普通牌组处理;其他值会返回 400 INVALID_ARGUMENT。删除接口默认只删除本机牌组,带 completelyDelete=1 时执行彻底删除,请确认数据备份后再使用。
牌组对象通常包含 id、type、name、thumb_url、description、theme、pkg_id、pkg_version、standalone、enabled、total_cards、created_at 和 updated_at。具体字段以当前版本的 /openapi.json 和实际响应为准。
七、目录接口
| 方法和路径 | 请求参数 | 返回 |
|---|---|---|
GET /decks/{deckId}/categories/list | 路径:deckId | 目录数组(已展开,不含 children) |
POST /decks/{deckId}/categories/create | JSON:name,可选 description、parentSN | 新建目录 |
POST /decks/{deckId}/categories/{categorySN}/update | JSON 或查询参数:name | 更新后的目录 |
POST /decks/{deckId}/categories/{categorySN}/delete | 无 | 文本 ok |
创建子目录示例:
POST /decks/42/categories/create
{
"name": "第四级",
"description": "可选说明",
"parentSN": "category-parent-sn"
}
也可以用 parent 或 category 传目录名称,桌面版会在当前牌组中查找同名目录;建议优先使用稳定的 parentSN。列表中的每个目录包含 id、sn、deck_id、parent_sn、name、description、level、order、cards_num、is_locked、时间和同步字段。
通过此兼容接口删除目录时不会递归删除子目录或卡片;非空目录(包含子目录或卡片)会返回 400 CATEGORY_NOT_EMPTY。需要删除前请先确认目录内容。
八、模板接口
| 方法和路径 | 请求参数 | 返回 |
|---|---|---|
GET /decks/{deckId}/templates/list | 路径:deckId | 模板数组 |
POST /decks/{deckId}/templates/create | JSON:type、name、fields、style、options;可选 description | 新建模板 |
GET /decks/{deckId}/templates/{templateSN}/info | 路径:deckId、templateSN | 模板对象 |
POST /decks/{deckId}/templates/{templateSN}/update | 可更新 name、description、fields、style、options 等字段 | 更新后的模板 |
POST /decks/{deckId}/templates/{templateSN}/delete | 无 | 文本 ok |
模板创建示例:
POST /decks/42/templates/create
{
"type": 0,
"name": "问答模板",
"description": "正面和背面",
"fields": [
{"name": "front", "data_type": 0, "required": true},
{"name": "back", "data_type": 1, "required": true}
],
"style": {"front": "front", "back": "back"},
"options": {}
}
fields、style 和 options 会按模板原样传递。更新请求的牌组 ID 和模板 SN 以路径为准,即使正文中带有同名字段也不会覆盖路径身份。模板对象还可能包含 prototype_id、is_default、cards_num、usn 和 synced_at 等字段。
九、卡片接口
| 方法和路径 | 请求参数 | 返回 |
|---|---|---|
POST /decks/{deckId}/cards/search | JSON:可选 query、limit | 兼容格式卡片数组 |
POST /decks/{deckId}/cards/create | JSON:style、data;可选标题、模板、目录、选项和顺序 | 新建或处理冲突后的卡片 |
GET /decks/{deckId}/cards/{cardSN}/info | 路径:deckId、cardSN | 兼容格式卡片 |
POST /decks/{deckId}/cards/{cardSN}/update | 可选 title、data、options、order、目录 | 更新后的兼容格式卡片 |
POST /decks/{deckId}/cards/{cardSN}/delete | 无 | 文本 ok |
卡片样式编号
style | 含义 |
|---|---|
0 | 基础卡(basic) |
1 | 简答卡(simple) |
10 | 笔记卡(note) |
30 | 单词卡(word) |
31 | 扩展单词卡(word-extended) |
60 | 问答卡(question) |
61 | 选择题(choice) |
62 | 判断题(judgement) |
创建卡片示例:
POST /decks/42/cards/create
{
"style": 1,
"title": "光合作用",
"templateSN": "template-sn",
"categorySN": "category-sn",
"order": 10,
"data": {
"front": {"type": 0, "data": "什么是光合作用?", "lang": "zh-CN"},
"back": {"type": 1, "data": "绿色植物利用光能制造有机物。", "lang": null}
},
"options": {}
}
字段包装中的 type 为 0 文本、1 HTML;lang 可为语言代码或 null。选择题的选项可使用 A 至 H 作为字段名;返回结果会兼容旧客户端,使用 @option.A 这类名称。卡片响应字段包括 id、sn、deck_id、category_sn、style、title、data、options、order、created_at、updated_at、_usn 和 _synced。
不传 templateSN 时,桌面版会按样式和字段名自动选择模板。categorySN 也可以用 categorySn、category_sn 或目录名称 category 传入。order 用于指定卡片顺序,正反卡应分别传入各自顺序。
同标题卡片的处理由 title_conflict 控制:
replace(默认):找到完全相同标题时更新已有卡片。ignore:找到完全相同标题时直接返回已有卡片,不创建或修改。keepboth:保留已有卡片并创建新卡片。
搜索接口的 limit 范围为 1 到 1000,默认 1000;query 会去除首尾空格。搜索和标题冲突检查返回的是主卡片,不包含反向卡片。
十、资源接口
| 方法和路径 | 请求参数 | 返回 |
|---|---|---|
POST /decks/{deckId}/resources/upload?filename=... | 查询参数 filename;正文为二进制文件 | 资源对象 |
POST /decks/{deckId}/resources/delete?name=... | 查询参数 name | 文本 ok |
POST /decks/42/resources/upload?filename=hello.mp3
Content-Type: application/octet-stream
X-API-SECRET: <访问秘钥>
<文件二进制内容>
资源上传不使用 JSON,也不要把文件内容 Base64 后再嵌入 JSON。资源名称应使用牌组内唯一、可复用的文件名;删除前请确认没有其他卡片引用该资源。返回字段以 Core 当前资源对象为准,常见字段包括 name、type、size、md5、url、refs、状态和同步时间。
十一、错误处理
失败响应统一为 JSON:
{
"error": {
"code": "FORBIDDEN",
"message": "访问秘钥无效"
}
}
| HTTP 状态 | 常见错误码 | 含义 |
|---|---|---|
| 400 | INVALID_ARGUMENT、INVALID_JSON、TEMPLATE_NOT_FOUND | 请求参数、JSON 或创建卡片时的模板匹配无效 |
| 401 | AUTH_REQUIRED、AUTH_INVALID | 桌面版未登录或登录状态失效 |
| 403 | FORBIDDEN、*_PERMISSION_DENIED、*_NOT_EDITABLE | 秘钥无效或当前数据不可操作 |
| 404 | NOT_FOUND、DECK_NOT_FOUND、TEMPLATE_NOT_FOUND、CARD_NOT_FOUND | 接口或查询的实体不存在(创建卡片未匹配模板属于 400) |
| 413 | PAYLOAD_TOO_LARGE | 请求正文超过大小限制 |
| 503 | Core 返回的可重试错误码 | 桌面版核心服务暂时不可用,请稍后重试 |
| 500 | INTERNAL_ERROR | 服务内部错误;响应不会泄露内部异常详情 |
十二、旧客户端兼容性
- 接口路径和卡片字段保持旧客户端使用的命名方式;桌面版内部会转换为 Core 的 typed 方法。
- 请求 JSON 中的
categorySN、templateSN、title_conflict、order等旧字段仍可使用,同时兼容对应的 camelCase 和 snake_case 写法。 - 响应会递归转换普通对象键名为 snake_case,但卡片
data、fields、options和style内部的自定义键名及值保持原样。 - 桌面版当前不提供
/auth/login或/auth/logout路由;登录请在桌面版界面完成。 - 如需程序化发现字段和参数,请以正在运行的桌面版返回的
/openapi.json为准。
十三、安全建议
- 只在可信局域网中开启客户端 API,避免把端口暴露到公网。
- 优先使用
X-API-SECRET请求头,不要把秘钥写入 URL、截图、脚本仓库或共享日志。 - 自动化批量修改前先备份或导出牌组;删除牌组、目录、卡片和资源前先在测试牌组验证。
- 修改本地数据后,按需调用牌组同步或全部同步接口,并等待同步完成。
- 不再使用时关闭客户端 API;怀疑秘钥泄露时立即重置秘钥。
十四、排查问题
| 问题 | 处理方式 |
|---|---|
/_check 无法访问 | 确认桌面版正在运行且客户端 API 已开启;检查局域网地址、防火墙和端口占用。 |
返回 403 FORBIDDEN | 检查 X-API-SECRET 或 secret 查询参数;重置秘钥后必须更新调用方配置。 |
返回 401 AUTH_REQUIRED | 在桌面版界面完成登录,并确认登录会话未失效。 |
返回 400 TEMPLATE_NOT_FOUND | 检查 style、templateSN 和字段名;不指定模板时确保牌组存在可匹配模板。 |
| 数据没有出现在其他设备 | 客户端 API 先修改本地数据;调用 POST /sync 或指定牌组同步后再检查云端。 |