API 文档

密钥管理

概述

外部系统通过 /openapi/v1 维护分类、项目与文档。所有请求需携带以下签名头:

Base URL:所有接口都基于 https://22qalpohd8v7x.app.jcloudcs.com/openapi/v1,请求需携带签名请求头

快速开始

使用开放 API 只需 3 步:

  1. 在 密钥管理 新建密钥,获得 AppKey 与 Secret
  2. 按下方签名算法构造 X-Signature 等 4 个请求头
  3. 调用 /openapi/v1 接口,以密钥属主身份读写文档

三种语言的完整签名请求示例(以文档列表接口为例):

cURL
# 先计算签名(详见下方「签名算法」)
TS=$(date +%s)  NONCE=$(openssl rand -hex 16)
SIGN=$(printf "%s\n%s\n%s\n%s\n%s\n%s" "$APP_KEY" "GET" "/openapi/v1/docs" "$TS" "$NONCE" \
  "$(printf '' | sha256sum | cut -d' ' -f1)" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')

curl "$BASE_URL/openapi/v1/docs?page=1&size=15" \
  -H "X-App-Key: $APP_KEY" \
  -H "X-Timestamp: $TS" \
  -H "X-Nonce: $NONCE" \
  -H "X-Signature: $SIGN"

签名请求头

请求头说明
X-App-Key身份标识(创建密钥时生成)
X-Timestamp当前 Unix 秒,与服务器偏差须在 5 分钟内
X-Nonce8~64 位随机串,5 分钟内不可重复(防重放)
X-Signature按下述算法计算的 hex 签名

签名算法

签名算法:sign = HMAC-SHA256(Secret, AppKey + LF + METHOD + LF + path + LF + timestamp + LF + nonce + LF + sha256hex(body)),LF 为换行符;无请求体时 body 为空串。

cURL
# 先计算签名(待签串见上方说明)
TS=$(date +%s)  NONCE=$(openssl rand -hex 16)
SIGN=$(printf "%s\n%s\n%s\n%s\n%s\n%s" "$APP_KEY" "GET" "/openapi/v1/docs" "$TS" "$NONCE" \
  "$(printf '' | sha256sum | cut -d' ' -f1)" | openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $NF}')

curl "https://22qalpohd8v7x.app.jcloudcs.com/openapi/v1/docs?page=1&size=15" \
  -H "X-App-Key: $APP_KEY" \
  -H "X-Timestamp: $TS" \
  -H "X-Nonce: $NONCE" \
  -H "X-Signature: $SIGN"

可用接口

所有操作均以密钥属主身份执行,只能访问本人的资源。成功返回 {"data": …},失败返回 {"error": "错误信息"}。

分类

GET /openapi/v1/categories 获取我的分类列表(按 sort、id 升序)
响应示例
JSON
{
  "data": [
    {
      "id": 1,
      "name": "产品文档",
      "sort": 0,
      "owner_id": 2,
      "doc_count": 5
    }
  ]
}
POST /openapi/v1/categories 新建分类;同级分类名不可重复
请求示例
JSON
{
  "name": "产品文档",
  "sort": 0
}
响应示例
JSON
{
  "data": {
    "id": 1,
    "name": "产品文档",
    "sort": 0,
    "owner_id": 2,
    "doc_count": 0
  }
}
PUT /openapi/v1/categories/:id 修改分类
请求示例
JSON
{
  "name": "产品文档(改名)",
  "sort": 10
}
响应示例
JSON
{
  "data": {
    "id": 1,
    "name": "产品文档(改名)",
    "sort": 10,
    "owner_id": 2,
    "doc_count": 5
  }
}
DELETE /openapi/v1/categories/:id 删除分类,其下文档回到未分类
响应示例
JSON
{
  "ok": true
}

项目

GET /openapi/v1/projects 获取我的项目列表(按更新时间倒序)
响应示例
JSON
{
  "data": [
    {
      "id": 3,
      "name": "2026 规划",
      "description": "年度规划文档",
      "owner_id": 2,
      "doc_count": 8,
      "created_at": "2026-01-05T10:00:00+08:00",
      "updated_at": "2026-09-01T14:30:00+08:00"
    }
  ]
}
POST /openapi/v1/projects 新建项目
请求示例
JSON
{
  "name": "2026 规划",
  "description": "年度规划文档"
}
响应示例
JSON
{
  "data": {
    "id": 3,
    "name": "2026 规划",
    "description": "年度规划文档",
    "owner_id": 2,
    "doc_count": 0,
    "created_at": "2026-09-24T09:00:00+08:00",
    "updated_at": "2026-09-24T09:00:00+08:00"
  }
}
PUT /openapi/v1/projects/:id 修改项目
请求示例
JSON
{
  "name": "2026 规划(修订)",
  "description": "年度规划与里程碑"
}
响应示例
JSON
{
  "data": {
    "id": 3,
    "name": "2026 规划(修订)",
    "description": "年度规划与里程碑",
    "owner_id": 2,
    "doc_count": 8,
    "updated_at": "2026-09-24T10:00:00+08:00"
  }
}
DELETE /openapi/v1/projects/:id 删除项目,其下文档回到未分组(不级联删除)
响应示例
JSON
{
  "ok": true
}

文档

GET /openapi/v1/docs 分页获取文档列表,不含正文
响应示例
JSON
{
  "data": [
    {
      "id": 12,
      "title": "接入指南",
      "version": "v1.0.0",
      "slug": "aB3xY9kQ",
      "owner_id": 2,
      "project_id": 3,
      "category_id": 1,
      "is_shared": false,
      "view_count": 16,
      "created_at": "2026-08-12T09:20:00+08:00",
      "updated_at": "2026-09-20T18:02:00+08:00"
    }
  ],
  "total": 42,
  "page": 1,
  "size": 20
}
GET /openapi/v1/docs/:id 获取文档详情,含 Markdown 正文
响应示例
JSON
{
  "data": {
    "id": 12,
    "title": "接入指南",
    "version": "v1.0.0",
    "slug": "aB3xY9kQ",
    "type": "markdown",
    "content": "# 接入指南\n\n正文 Markdown…",
    "owner_id": 2,
    "project_id": 3,
    "category_id": 1,
    "is_shared": false,
    "view_count": 16,
    "created_at": "2026-08-12T09:20:00+08:00",
    "updated_at": "2026-09-20T18:02:00+08:00"
  }
}
POST /openapi/v1/docs 新建文档;type 可选 markdown(默认)/ mindmap / board,content 为对应类型内容;version 由系统自动生成(v1.0.0 起,每次内容保存 +1)
请求示例
JSON
{
  "title": "接入指南",
  "type": "markdown",
  "content": "# 接入指南\n\n正文 Markdown…",
  "project_id": 3,
  "category_id": 1
}
响应示例
JSON
{
  "data": {
    "id": 12,
    "title": "接入指南",
    "version": "v1.0.0",
    "slug": "aB3xY9kQ",
    "content": "# 接入指南\n\n正文 Markdown…",
    "owner_id": 2,
    "project_id": 3,
    "category_id": 1,
    "is_shared": false,
    "view_count": 0
  }
}
POST /openapi/v1/docs/from-url 从公网链接抓取正文并创建可编辑的 Markdown 文档(JSON:url 必填,title / project_id / category_id 可选;拒绝内网与保留地址)
请求示例
JSON
{
  "url": "https://example.com/guide",
  "title": "接入指南",
  "project_id": 3,
  "category_id": 1
}
响应示例
JSON
{
  "data": {
    "id": 14,
    "title": "接入指南",
    "version": "v1.0.0",
    "slug": "mN4pQ8sT",
    "type": "markdown",
    "content": "# 接入指南\n\n> 来源:https://example.com/guide\n\n正文…",
    "owner_id": 2,
    "project_id": 3,
    "category_id": 1,
    "is_shared": false,
    "view_count": 0
  }
}
POST /openapi/v1/docs/html 上传 zip 创建 HTML 整站文档(multipart 表单:title、file、project_id?、category_id?;站点根须含 index.html)。站点文件不支持在线编辑,可经更新接口改标题/归属
请求示例
HTTP
Content-Type: multipart/form-data; boundary=----docshare

------docshare
Content-Disposition: form-data; name="title"

产品官网
------docshare
Content-Disposition: form-data; name="file"; filename="dist.zip"
Content-Type: application/zip

<zip 二进制内容>
------docshare--
响应示例
JSON
{
  "data": {
    "id": 13,
    "title": "产品官网",
    "slug": "kQ3xZ9mP",
    "type": "html",
    "content": "{\"prefix\":\"html/13/ab3f/\",\"entry\":\"index.html\",\"files\":[\"index.html\"]}",
    "owner_id": 2,
    "project_id": 3,
    "category_id": 1,
    "is_shared": false,
    "view_count": 0
  }
}
POST /openapi/v1/docs/html-page 创建单页 HTML(试卷/简历等):JSON 传 title + 完整自包含 html(CSS/JS 内联);页面写入对象存储,content 为 manifest。与站内智能体 create_html_page 对齐
请求示例
JSON
{
  "title": "一年级数学卷",
  "html": "<!DOCTYPE html><html><head><meta charset=\"utf-8\"><style>body{font-family:sans-serif}</style></head><body><h1>试卷</h1></body></html>",
  "project_id": 3,
  "category_id": 1
}
响应示例
JSON
{
  "data": {
    "id": 15,
    "title": "一年级数学卷",
    "slug": "pL7nR2wX",
    "type": "html",
    "content": "{\"prefix\":\"html/15/cd9e/\",\"entry\":\"index.html\",\"files\":[\"index.html\"],\"size\":120,\"count\":1}",
    "owner_id": 2,
    "project_id": 3,
    "category_id": 1,
    "is_shared": false,
    "view_count": 0
  }
}
GET /openapi/v1/docs/:id/html-page 读取单文件 HTML 文档的入口页原文(仅单页站点;多文件整站请用 GET /docs/:id 看 manifest)
响应示例
JSON
{
  "data": {
    "id": 15,
    "title": "一年级数学卷",
    "type": "html",
    "html": "<!DOCTYPE html><html>…</html>",
    "content_version": 0,
    "version": "v1.0.0",
    "project_id": 3,
    "category_id": 1
  }
}
PUT /openapi/v1/docs/:id/html-page 整页替换单文件 HTML 文档入口页(JSON:html + base_version,取自 GET 的 content_version;冲突返回 409;仅属主/管理员)。与站内智能体 replace_html_page 对齐
请求示例
JSON
{
  "html": "<!DOCTYPE html><html><head><meta charset=\"utf-8\"></head><body><h1>修订版</h1></body></html>",
  "base_version": 0
}
响应示例
JSON
{
  "data": {
    "id": 15,
    "content_version": 1,
    "version": "v1.0.1",
    "manifest": {
      "prefix": "html/15/ef12/",
      "entry": "index.html",
      "files": ["index.html"],
      "size": 98,
      "count": 1
    }
  }
}
PUT /openapi/v1/docs/:id 修改文档;未传字段不修改,project_id / category_id 传 0 表示清空归属;version 由系统自动递增,传入值被忽略
请求示例
JSON
{
  "title": "接入指南(修订)",
  "content": "# 接入指南 v2\n\n更新后的正文…",
  "project_id": 0,
  "category_id": 1
}
响应示例
JSON
{
  "data": {
    "id": 12,
    "title": "接入指南(修订)",
    "version": "v1.0.1",
    "slug": "aB3xY9kQ",
    "type": "markdown",
    "content": "# 接入指南 v2\n\n更新后的正文…",
    "owner_id": 2,
    "project_id": 0,
    "category_id": 1,
    "is_shared": false,
    "view_count": 16
  }
}
DELETE /openapi/v1/docs/:id 删除文档
响应示例
JSON
{
  "ok": true
}
GET /openapi/v1/docs/:id/share 查询文档分享状态(仅属主/管理员)
响应示例
JSON
{
  "data": {
    "enabled": true,
    "url": "https://example.com/s/0123abcd0123abcd0123abcd0123abcd",
    "share_token": "0123abcd0123abcd0123abcd0123abcd",
    "has_password": true,
    "can_edit": false,
    "expire_at": null
  }
}
POST /openapi/v1/docs/:id/share 开启或更新分享链接;can_edit 默认 false(登录用户不可编辑),password 留空表示不修改/无密码,expire_days 0=永久
请求示例
JSON
{
  "enabled": true,
  "can_edit": false,
  "password": "a1b2c3",
  "expire_days": 7
}
响应示例
JSON
{
  "ok": true,
  "enabled": true,
  "url": "https://example.com/s/0123abcd0123abcd0123abcd0123abcd",
  "hasPassword": false
}
DELETE /openapi/v1/docs/:id/share 关闭文档分享并清除访问申请
响应示例
JSON
{
  "ok": true
}

模板

GET /openapi/v1/templates 获取模板列表(自己创建 + 绑定到参与项目的,按更新时间倒序,不含内容)
响应示例
JSON
{
  "data": [
    {
      "id": 5,
      "name": "周报模板",
      "type": "markdown",
      "owner_id": 2,
      "project_id": 3,
      "category_id": 1,
      "created_at": "2026-09-28T10:00:00+08:00",
      "updated_at": "2026-09-28T10:00:00+08:00"
    }
  ],
  "total": 1,
  "page": 1,
  "size": 20
}
GET /openapi/v1/templates/:id 获取模板详情,含内容;可见即可读(属主 / 绑定项目的成员 / 管理员)
响应示例
JSON
{
  "data": {
    "id": 5,
    "name": "周报模板",
    "type": "markdown",
    "content": "# 周报\n\n## 本周进展\n…",
    "owner_id": 2,
    "project_id": 3,
    "category_id": 1
  }
}
POST /openapi/v1/templates 新建模板;type 支持 markdown(默认)/ mindmap / board / drawio(html 整站不支持);project_id 绑定项目后项目成员可共用
请求示例
JSON
{
  "name": "周报模板",
  "type": "markdown",
  "content": "# 周报\n\n## 本周进展\n…",
  "project_id": 3,
  "category_id": 1
}
响应示例
JSON
{
  "data": {
    "id": 5,
    "name": "周报模板",
    "type": "markdown",
    "owner_id": 2,
    "project_id": 3,
    "category_id": 1
  }
}
PUT /openapi/v1/templates/:id 修改模板(仅属主 / 管理员);未传字段不修改,project_id / category_id 传 0 表示解除绑定
请求示例
JSON
{
  "name": "周报模板(修订)",
  "content": "# 周报 v2\n…"
}
响应示例
JSON
{
  "data": {
    "id": 5,
    "name": "周报模板(修订)",
    "type": "markdown",
    "owner_id": 2,
    "project_id": 3,
    "category_id": 1
  }
}
DELETE /openapi/v1/templates/:id 删除模板,不影响已创建的文档
响应示例
JSON
{
  "ok": true
}

错误码

下表 error 信息为服务器原文返回,HTTP 状态码同时标注。

通用错误码(鉴权与协议层)

状态码error 信息
401 缺少签名头:X-App-Key / X-Timestamp / X-Nonce / X-Signature
401 时间戳无效或与服务器偏差超过 5 分钟
401 nonce 长度需在 8~64 之间
401 AppKey 不存在或已禁用
400 读取请求体失败或超过 2MB
401 签名校验失败
401 nonce 已使用,疑似重放请求
401 密钥属主不存在或已禁用

业务错误码

状态码error 信息
400 参数错误
400 标题不能为空
400 项目或分类非法
400 项目非法或无权归属到该项目
400 分类非法或无权归属到该分类
400 创建失败,你的分类名已存在
400 更新失败,你的分类名已存在
400 分类 id 非法
400 项目 id 非法
403 无权操作他人的分类
403 无权操作他人的项目
403 无权操作他人的文档
404 分类不存在
404 项目不存在
404 文档不存在
400 模板名称不能为空
400 模板名称不能超过 100 字
400 不支持的模板类型(HTML 整站不支持模板)
400 模板内容过大(超过 2MB)
400 模板 id 非法
403 无权操作他人的模板
404 模板不存在
500 创建失败
500 更新失败
500 删除失败