Docspell 0.43.0 API 使用详解

Docspell 0.43.0 REST API 全端点族实测:四种认证方式、注册登录与会话语义、上传检索标签、Source URL 导入、分享短链生命周期、admin/integration 端点与目录监视,curl 请求与响应逐条回显

DocspellDocspell文档管理自托管OCRJVM2026/11/01

Docspell 0.43.0 API 使用详解

目标:把 Docspell 0.43.0 的 REST API 全端点族跑通并逐条留证——四种认证方式、注册登录与会话语义、上传/改名/确认/下载条目生命周期、结构化检索与全文检索、标签、Source 与 URL 导入、分享短链完整生命周期(含过期)、admin 与 integration 端点、目录监视摄取链路。文中每一条 curl 命令与响应回显均为 2026-11-01 在真实部署上的实测输出(示例账号 admin / 密码 q1w2e3r4,入口 http://localhost:8137)。环境搭建见《Docspell Docker Compose 部署指南》,相关配置参数见《Docspell 0.43.0 配置参数详解》。

一、环境信息

项目值验证命令
Docspellv0.43.0(gitCommit 6b4e03ac,2025-03-15 构建)curl http://localhost:8137/api/info/version
API 前缀http://localhost:8137/api/v1下文所有请求
接口规格openapi.yml 共 168 个路径(https://docspell.org/openapi/docspell-openapi.yml,2026-11-01 抓取)见第八章
活文档http://localhost:8137/api/doc(Swagger UI,随版本走)浏览器打开
演示账号admin / q1w2e3r4(集体 admin,成员 admin、editor)见 4.1/5.1
客户端工具curl 8.x + python3(解析 JSON 回显)—
$ curl -s http://localhost:8137/api/info/version
{"version":"0.43.0","builtAtMillis":1742053616280,"builtAtString":"2025-03-15 16:46:56.280+0100","gitCommit":"6b4e03ac3a2ac013783ec42245c01b7c0ff0dca9","gitVersion":"0.43.0"}

二、前置条件

  1. Docspell 栈已部署且 restserver/joex 正常(《Docspell Docker Compose 部署指南》);
  2. 已完成首次注册(本文 4.1 会从零演示);
  3. integration 端点已启用并设好 header 值(配置键 docspell.server.integration-endpoint.*,本文实测值 q1w2e3r4,见 5.8);
  4. admin 端点 secret 已设置(配置键 docspell.server.admin-endpoint.secret,见 5.7)。

三、API 总览

3.1 路由分组与认证要求

Docspell 的 168 个端点按路径前缀分四族,认证要求各不相同:

前缀族认证要求
/api/v1/open/*开放无需认证(登录、注册、source URL 上传、分享验证)
/api/v1/sec/*已登录头 X-Docspell-Auth: <token> 或 cookie docspell_auth
/api/v1/share/*分享头 Docspell-Share-Auth: <share-token>
/api/v1/admin/*管理头 Docspell-Admin-Secret: <secret>(secret 为空则整族禁用)

3.2 认证方式

💡 技术拓展:Docspell 的 API 认证没有采用业界更常见的 Authorization: Bearer 标准头,而是四个自定义头各管一族——X-Docspell-Auth(用户 token)、Docspell-Share-Auth(分享 token)、Docspell-Admin-Secret(静态密钥)、Docspell-Integration(静态密钥,可选叠加 HTTP Basic 或 IP 白名单)。同类方案对比:Bearer token(标准、网关友好)、HTTP Basic(每次带账密)、API Key 头(Docspell 的 admin/integration 即此类)、mTLS(最强但运维重)。写客户端时注意 token 走自定义头这点。

token 形态(4.2 实测回显可见):签发时间戳-base64(账号信息)-$2a$10$…,服务端不存会话、签名校验即放行;有效期由配置 docspell.server.auth.session-valid 决定(默认 5 分钟,实测 validMs: 300000)。

3.3 响应与限流约定

  • 多数写操作返回统一结构 {"success": true/false, "message": "…"}(BasicResult);
  • 认证失败/禁用端点返回 403;路径不存在返回 404 + {"success":false,"message":"Not found"};
  • 请求体字段不全时返回 500 空响应体(不是 400),真实原因只写服务端日志——这是本文实测踩坑最多的形态,见 7.2;
  • 无显式速率限制;并发上限由 server-options.max-connections(默认 1024)控制,搜索单页由 max-item-page-size(默认 200)封顶(配置详解见《Docspell 0.43.0 配置参数详解》)。

四、认证与首次调用

4.1 注册首个集体

$ curl -s -X POST http://localhost:8137/api/v1/open/signup/register -H "Content-Type: application/json" -d "{\"collectiveName\": \"admin\", \"login\": \"admin\", \"password\": \"q1w2e3r4\"}"
{"success":true,"message":"Signup successful"}

重复注册同名集体被拒(回显即防重放证据):

$ curl -s -X POST http://localhost:8137/api/v1/open/signup/register -H "Content-Type: application/json" -d "{\"collectiveName\":\"admin\",\"login\":\"editor\",\"password\":\"q1w2e3r4\"}"
{"success":false,"message":"A collective with this name already exists."}

💡 说明:collectiveName+login 相同即"集体同名同用户"(个人模式);给已有集体加第二个成员不走这个端点,走登录态的 POST /sec/user(见 5.1)。注册策略 open/invite/closed 由服务端配置决定,实测三态行为见《Docspell 0.43.0 配置参数详解》注册策略一节。

4.2 登录取 token

$ curl -s -X POST http://localhost:8137/api/v1/open/auth/login -H "Content-Type: application/json" -d "{\"account\": \"admin\", \"password\": \"q1w2e3r4\"}"
{"collective":"admin","user":"admin","success":true,"message":"Login successful","token":"1793535919263-MS9hZG1pbi9IRDZaNkJEUFEzai1BWWJpbVRWMXlXRi1tSlVidzhVZWhEdy1QS1RXeWJlZ1RvNS9hZG1pbg==-false-$2a$10$QhJTQpsU9gXdRtSdVlcRIe-pesNo6DW0D0TbNlMIaMOYP24xLA=","validMs":300000,"requireSecondFactor":false}

account 支持两种写法:只写集体名(当 login==collective)或 集体/用户 全名(5.1 登录 editor 用后者)。

4.3 会话刷新

token 未过期前用旧 token 换新 token(Web 界面即按 validMs 周期调用):

$ curl -s -X POST -H "X-Docspell-Auth: <token>" http://localhost:8137/api/v1/sec/auth/session
{"collective": "admin", "user": "admin", "success": true, "message": "Login successful", "token": "1793540673510-MS9hZG1pbi…<截断>", "validMs": 300000, "requireSecondFactor": false}

4.4 登出与无状态语义(实测反直觉点)

$ curl -s -X POST -H "X-Docspell-Auth: <token>" http://localhost:8137/api/v1/sec/auth/logout
$ curl -s -o /dev/null -w "%{http_code}" -H "X-Docspell-Auth: <旧token>" http://localhost:8137/api/v1/sec/tag
200

logout 之后旧 token 依然能调通 sec 端点(直到 validMs 自然过期)。这不是 bug:openapi 对 logout 的原文说明是 "This is not strictly necessary"——token 自包含签名、服务端无会话表,登出只是清客户端 cookie。要即时吊销只能等过期或轮换 auth.server-secret(后者会吊销全体,见《Docspell 0.43.0 配置参数详解》auth 一节)。

五、各 API 域详解

5.1 用户与集体

加成员 POST /sec/user。踩坑实测:只传 login+password 会被静默拒绝(500 空体,服务端日志 DecodingFailure at : DownField(id))——openapi 的 User schema required 字段是 id/login/state/source/loginCount/created,全量补齐才成功:

$ curl -s -X POST -H "X-Docspell-Auth: <token>" -H "Content-Type: application/json" -d "{\"id\":\"\",\"login\":\"editor\",\"state\":\"active\",\"source\":\"local\",\"loginCount\":0,\"created\":1793540895813,\"password\":\"q1w2e3r4\"}" http://localhost:8137/api/v1/sec/user
{"success":true,"message":"User created."}

$ curl -s -H "X-Docspell-Auth: <token>" http://localhost:8137/api/v1/sec/user
[{"login": "admin", "state": "active", "source": "local"}, {"login": "editor", "state": "active", "source": "local"}]

id:"" 表示由服务端生成;created 传当前毫秒时间戳。新成员用全名登录:

$ curl -s -X POST http://localhost:8137/api/v1/open/auth/login -H "Content-Type: application/json" -d "{\"account\":\"admin/editor\",\"password\":\"q1w2e3r4\"}"
{"collective": "admin", "user": "editor", "success": true, "message": "Login successful", "token": "1793540895962-MS9hZG…<截断>", "validMs": 300000, "requireSecondFactor": false}

集体统计 GET /sec/collective/insights(条数、体积、标签云):

$ curl -s -H "X-Docspell-Auth: <token>" http://localhost:8137/api/v1/sec/collective/insights
{"incomingCount":5,"outgoingCount":1,"deletedCount":0,"itemSize":26861,"tagCloud":{"items":[{"tag":{"id":"BWfHJnZ9ipt-3NeKsaW4bmr-Pqj6fZvWZeP-ZhDmwU4yrEq","name":"invoice","category":null,"created":1793537206738},"count":2}]}}

5.2 上传与条目生命周期

上传 POST /sec/upload/item,multipart 两个 part:meta(ItemUploadMeta JSON,multiple 必填)+ file(至少一个):

$ curl -s -X POST -H "X-Docspell-Auth: <token>" -F "meta={\"multiple\":false,\"direction\":\"outgoing\"};type=application/json" -F "file=@d5-note.txt" http://localhost:8137/api/v1/sec/upload/item
{"success":true,"message":"Files submitted."}

meta 还支持 folder、skipDuplicates、tags、fileFilter、language、attachmentsOnly 等可选键(openapi ItemUploadMeta)。上传是异步的:响应只代表"已入队",条目要经 joex 处理(转 PDF、抽文本、FTS 索引)。

定位新条目 id(连字符名不能直接当查询词,原因见 5.3 与 7.1,这里用空查询列表后按 name 过滤):

$ curl -s -H "X-Docspell-Auth: <token>" "http://localhost:8137/api/v1/sec/item/search?q=&withDetails=true"
2026-11 [('CUo2WaK88DH-DzV6jEkCLsz-92GhSyECsuq-2zXjFd4sQ4h', 'd5-note.txt', 'created', 'webapp'), ('X5aQqkdnXR5-2w7t5GZKR8F-jXzBaXX4YNu-aXs1BDo1uQ', 'consume3.txt', 'created', 'integration'), ('BuKVnmgPxGe-uueesMGWKJH-8rC7jcttYeS-QRwhxHjDiKn', 'consume2.txt', 'created', 'integration'), ('SnYyveHMUJb-muJy88SDHYP-T6M3bbzHQ63-GANdKEuXGT', 'integration-note.txt', 'created', 'integration'), ('DRBnvpvRfGV-oqdwHc1Jjpi-CW9MzZ3DeN2-Nby6RLJ5M59', 'import-note.txt', 'created', 'webapp')]

改名 / 确认 / 详情:

$ curl -s -X PUT -H "X-Docspell-Auth: <token>" -H "Content-Type: application/json" -d '{"text":"D5-outgoing-invoice"}' http://localhost:8137/api/v1/sec/item/CUo2WaK88DH-DzV6jEkCLsz-92GhSyECsuq-2zXjFd4sQ4h/name
{"success":true,"message":"Name updated"}

$ curl -s -X POST -H "X-Docspell-Auth: <token>" http://localhost:8137/api/v1/sec/item/CUo2WaK88DH-DzV6jEkCLsz-92GhSyECsuq-2zXjFd4sQ4h/confirm
{"success":true,"message":"Item data confirmed"}

$ curl -s -H "X-Docspell-Auth: <token>" http://localhost:8137/api/v1/sec/item/CUo2WaK88DH-DzV6jEkCLsz-92GhSyECsuq-2zXjFd4sQ4h
{
  "id": "CUo2WaK88DH-DzV6jEkCLsz-92GhSyECsuq-2zXjFd4sQ4h",
  "name": "D5-outgoing-invoice",
  "state": "confirmed",
  "direction": "outgoing",
  "source": "webapp",
  "attachments": [
    {
      "id": "Hm7VpDnYxDq-nGSHczs67Kr-L1ZNDNxS2ME-5WVCTvxPrZU",
      "name": "d5-note.txt",
      "size": 70,
      "contentType": "text/plain; charset=\"ISO-8859-1\"",
      "converted": false
    }
  ]
}

state 从 created(刚处理完)到 confirmed(人工确认元数据);条目族还有 /direction、/tags、/taglink、/note 等 PUT/POST 子路径。

下载附件原件 GET /sec/attachment/{id}/original:

$ curl -s -o d5-dl.bin -w "%{http_code} %{size_download}" -H "X-Docspell-Auth: <token>" http://localhost:8137/api/v1/sec/attachment/Hm7VpDnYxDq-nGSHczs67Kr-L1ZNDNxS2ME-5WVCTvxPrZU/original
200 70
$ file d5-dl.bin
d5-dl.bin: ASCII text

另有 /converted(转 PDF 后的预览)与 /thumbnail 变体。

队列观测 GET /sec/queue/state(节选,作业全日志可见):

$ curl -s -H "X-Docspell-Auth: <token>" http://localhost:8137/api/v1/sec/queue/state
{
  "progress": [],
  "completed": [
    {
      "id": "AS4LYBF8CNS-F45pazyD9am-LhyB75zWZFH-A4cPAxxSayB",
      "name": "d5-note.txt",
      "submitted": 1793540673817,
      "priority": "high",
      "state": "success",
      "retries": 0,
      "logs": [
        {
          "time": 1793540674314,
          "level": "info",
          "message": "============ Start processing d5-note.txt ============"
        },
        {
          "time": 1793540674329,
          "level": "debug",
          "message": "Not checking for duplicates"
        },
        {
          "time": 1793540674363,
          "level": "info",
          "message": "Creating new item with 1 attachment(s)"
        },
        {
          "time": 1793540674416,
          "level": "info",
          "message": "Creating item finished in 53 ms"
        },
        …(后续 logs 条目与 queued 数组略,输出节选)

顶层三数组 progress/completed/queued,每个作业带 state(success/failed/stuck)、retries 与逐条 logs——排障第一现场。

5.3 检索与查询语言

基础列表 GET /sec/item/search(q= 空即全部,按月份分组返回):

$ curl -s -H "X-Docspell-Auth: <token>" "http://localhost:8137/api/v1/sec/item/search?q=&withDetails=true"
{
    "groups": [
        {
            "name": "2026-11",
            "items": [
                {
                    "id": "DRBnvpvRfGV-oqdwHc1Jjpi-CW9MzZ3DeN2-Nby6RLJ5M59",
                    "name": "import-note.txt",
                    "state": "created",
                    "date": 1793535933451,
                    "dueDate": null,
                    "source": "webapp",
                    "direction": "incoming",
                    "corrOrg": null,
                    "corrPerson": null,
                    "concPerson": null,
                    "concEquipment": null,
                    "folder": null,
                    "attachments": [
                        {
                            "id": "7D9MRpPC1Nn-ukzRVAmb5bk-UZ6EkwB9262-vpDbdQVwJ3v",
                            "position": 0,
                            "name": "import-note.converted.pdf",
                            "pageCount": 1
                        }
                    ],
                    "tags": [],
                    "customfields": [],
                    "relatedItems": [],
                    "notes": null,
                    "highlighting": []
                }
            ]
        }
    ],
    "limit": 200,
    "offset": 0,
    "limitCapped": false
}

(该回显为首轮实测,当时条目尚未挂标签、joex 已生成转换附件 import-note.converted.pdf。)

查询语言:q 是 Docspell 自研的小 DSL,条件默认以 &(AND)组合。实测可用形态:

# 标签过滤
$ curl -s -H "X-Docspell-Auth: <token>" "http://localhost:8137/api/v1/sec/item/search?q=tag=invoice"
{"groups":[{"name":"2026-11","items":[{"id":"DRBnvpvRfGV-oqdwHc1Jjpi-CW9MzZ3DeN2-Nby6RLJ5M59","name":"import-note.txt","state":"created","date":1793535933451,"dueDate":null,"source":"webapp","direction":"incoming","corrOrg":null,"corrPerson":null,"concPerson":null,"concEquipment":null,"folder":null,"attachments":[],"tags":[],"customfields":[],"relatedItems":[],"notes":null,"highlighting":[]}]}],"limit":200,"offset":0,"limitCapped":false}

# 标签 + 年份组合
$ curl -s -H "X-Docspell-Auth: <token>" --get --data-urlencode "q=tag=invoice year:2026" http://localhost:8137/api/v1/sec/item/search
{"groups":[{"name":"2026-11","items":[{"id":"DRBnvpvRfGV-oqdwHc1Jjpi-CW9MzZ3DeN2-Nby6RLJ5M59","name":"import-note.txt","state":"created","date":1793535933451,"dueDate":null,"source":"webapp","direction":"incoming","corrOrg":null,"corrPerson":null,"concPerson":null,"concEquipment":null,"folder":null,…(后续字段略,输出节选)

# 全文检索(content: 前缀,走 Solr)
$ curl -s -H "X-Docspell-Auth: <token>" --get --data-urlencode "q=content:consume2" http://localhost:8137/api/v1/sec/item/search
groups: [('Results', ['consume2.txt'])]
$ curl -s -H "X-Docspell-Auth: <token>" --get --data-urlencode "q=content:\"cloud services\"" http://localhost:8137/api/v1/sec/item/search
groups: [('Results', ['import-note.txt'])]

词法要素(openapi + 实测归纳):tag= / tag!=、year: / month:、date: / after: / before: / due:、dir:in|out、state:confirmed、names:(文件名)、content:(FTS);组合词 &、|、!、括号。裸词不是全文检索——q=consume2 直接报解析错:

$ curl -s -H "X-Docspell-Auth: <token>" "http://localhost:8137/api/v1/sec/item/search?q=d5-note"
{"success":false,"message":"Invalid query: ParseFailure((& d5-note ),3,NonEmptyList(ExpectMessage(3,List(!, (&, (and, (or, (|, attach.id, cat, checksum, f),false)))

(该回显同时暴露连字符陷阱:d5-note 中的 - 被解析器当取反前缀,见 7.1。)FTS 词只能出现在根/表达式位置,( | tag=invoice content:consume2 ) 会报 A fulltext search may only appear in the root and expression.(实测)。

POST /sec/item/search 用 JSON body(ItemQuery:query/offset/limit/withDetails/searchMode)替代 GET 参数,语义相同;searchMode 枚举实测为 normal/trashed/all——传错值(如 simple)500,服务端日志 Invalid search mode: simple。

5.4 标签

建标签同样有"required 字段"坑:只传 {"name":"invoice"} 静默失败(GET 列表仍空),按 Tag schema 补 id/created 才成功:

$ curl -s -X POST -H "X-Docspell-Auth: <token>" -H "Content-Type: application/json" -d "{\"id\":\"0\",\"name\":\"invoice\",\"created\":1793536000000}" http://localhost:8137/api/v1/sec/tag
{"success":true,"message":"Tag successfully created."}

挂标签 PUT /sec/item/{id}/taglink(body 为标签标识数组):

$ curl -s -X PUT -H "X-Docspell-Auth: <token>" -H "Content-Type: application/json" -d '{"items":["invoice"]}' http://localhost:8137/api/v1/sec/item/DRBnvpvRfGV-oqdwHc1Jjpi-CW9MzZ3DeN2-Nby6RLJ5M59/taglink
{"success":true,"message":"Tags linked"}

⚠️ 注意:taglink 返回 Tags linked 不代表真的挂上了——首轮实测中该调用成功但 q=tag=invoice 查出 {"groups":[],"limit":200,"offset":0,"limitCapped":false}(空结果),修正标签创建方式后重挂才命中。挂标签后务必用检索或详情回显验证(5.3 的 q=tag=invoice 命中回显即验证态)。

删标签 DELETE /sec/tag/{id}:

$ curl -s -X DELETE -H "X-Docspell-Auth: <token>" http://localhost:8137/api/v1/sec/tag/<tagid>
{"success":true,"message":"Tag successfully deleted."}
$ curl -s -H "X-Docspell-Auth: <token>" http://localhost:8137/api/v1/sec/tag
{"count":1,"items":[{"id":"BWfHJnZ9ipt-3NeKsaW4bmr-Pqj6fZvWZeP-ZhDmwU4yrEq","name":"invoice","category":null,"created":1793537206738}]}

5.5 Source 与 URL 导入

Source 是"免登录上传通道":给集体建一个带 id 的来源,任何知道 URL 的人 POST 文件即入库(可绑自动标签)。

建 Source POST /sec/source。首轮只传 source 业务字段 → 500 空体(日志 DecodingFailure: DownField(id),与 5.1 同型坑);补 id:"" 与 created 后成功:

$ curl -s -X POST -H "X-Docspell-Auth: <token>" -H "Content-Type: application/json" -d "{\"source\":{\"id\":\"\",\"abbrev\":\"d5src\",\"description\":\"D5 url import\",\"enabled\":true,\"priority\":\"high\",\"counter\":0,\"attachmentsOnly\":false,\"created\":1793540895813},\"tags\":[\"invoice\"]}" http://localhost:8137/api/v1/sec/source
{"success":true,"message":"Source added."}

$ curl -s -H "X-Docspell-Auth: <token>" http://localhost:8137/api/v1/sec/source
[{"id": "HmunSevSa3C-d94cxkdqC2E-rskRj3fxArX-y2SW4DuYVqe", "abbrev": "d5src", "counter": 0, "tags": ["invoice"]}]

URL 上传 POST /open/upload/item/{sourceId}(open 族,无需 token):

$ curl -s -X POST -F file=@d5-src-note.txt http://localhost:8137/api/v1/open/upload/item/HmunSevSa3C-d94cxkdqC2E-rskRj3fxArX-y2SW4DuYVqe
{"success":true,"message":"Files submitted."}

效果验证(条目 source 显示 abbrev、自动挂上 Source 绑定的标签):

$ curl -s -H "X-Docspell-Auth: <token>" "http://localhost:8137/api/v1/sec/item/search?q=&withDetails=true"   # 过滤新条目
{"name": "d5-src-note.txt", "source": "d5src", "tags": [{"id": "BWfHJnZ9ipt-3NeKsaW4bmr-Pqj6fZvWZeP-ZhDmwU4yrEq", "name": "invoice", "category": null, "created": 1793537206738}]}

5.6 分享短链(share)

分享 = 把一个查询结果集打包成有时效的外部访问链接。

创建 POST /sec/share(publishUntil 为 epoch 毫秒)。空 query 会 500(日志 Failed to read input near 0——查询 DSL 不接受空串),必须给合法查询:

$ curl -s -X POST -H "X-Docspell-Auth: <token>" -H "Content-Type: application/json" -d '{"query":"tag=invoice","enabled":true,"publishUntil":1793541274534}' http://localhost:8137/api/v1/sec/share
{"success":true,"message":"New share created.","id":"4CsDvZ3E7oN-M92k8fAahny-3b7oSuZqKLV-zcXrHQby8qR"}

$ curl -s -H "X-Docspell-Auth: <token>" http://localhost:8137/api/v1/sec/share/4CsDvZ3E7oN-M92k8fAahny-3b7oSuZqKLV-zcXrHQby8qR
{"id":"4CsDvZ3E7oN-M92k8fAahny-3b7oSuZqKLV-zcXrHQby8qR","query":"(& tag=invoice )","owner":{"id":"HD6Z6BDPQ3j-AYbimTV1yWF-mJUbw8UehDw-PKTWybegTo5","name":"admin"},"name":null,"enabled":true,"publishAt":1793540674557,"publishUntil":1793541274534,"expired":false,"password":false,"views":0,"lastAccess":null}

服务端会把 tag=invoice 规范化为 (& tag=invoice );views/lastAccess 随外部访问递增。

验证取 share token POST /open/share/verify(真实用户拿 shareId 走的入口,可配密码则带 password 字段):

$ curl -s -X POST -H "Content-Type: application/json" -d '{"shareId":"4CsDvZ3E7oN-M92k8fAahny-3b7oSuZqKLV-zcXrHQby8qR"}' http://localhost:8137/api/v1/open/share/verify
{"success":true,"token":"1793540674655-NENzRHZaM0U3b04tTTkyazhmQWFobnktM2I3b1N1WnFLTFYtemNYckhRYnk4cVI=-$2a$10$iE2t6hecgukQJkg47n.hmO-5sUTwca5fQS+s2KCK+uPNlH03BE=","passwordRequired":false,"message":"Success","name":null}

用 share token 检索 POST /share/search/query(注意 searchMode 用合法枚举 normal,见 5.3):

$ curl -s -H "Docspell-Share-Auth: <share-token>" -H "Content-Type: application/json" -d '{"query":"","offset":0,"limit":20,"withDetails":false,"searchMode":"normal"}' http://localhost:8137/api/v1/share/search/query
[[["d5-src-note.txt", "d5src"], ["import-note.txt", "webapp"]]]

过期全链路:建一个 20 秒后过期的分享 → 有效期内 verify 成功 → sleep 25 后 verify 失败、详情 expired:true、share 检索直接 403:

$ curl -s -X POST -H "X-Docspell-Auth: <token>" -H "Content-Type: application/json" -d '{"query":"tag=invoice","enabled":true,"publishUntil":1793540924217}' http://localhost:8137/api/v1/sec/share   # 有效期 now+20s
{"success":true,"message":"New share created.","id":"3qLtFf5ybne-4Mmc13SYJqY-WTAVuKWLuCN-aKJgMCY5BVX"}

$ curl -s -X POST -H "Content-Type: application/json" -d '{"shareId":"3qLtFf5ybne-4Mmc13SYJqY-WTAVuKWLuCN-aKJgMCY5BVX"}' http://localhost:8137/api/v1/open/share/verify   # 有效期内
{"success": true, "token": "1793540904268-M3FMdE…<截断>", "passwordRequired": false, "message": "Success", "name": null}

$ sleep 25; curl -s -X POST -H "Content-Type: application/json" -d '{"shareId":"3qLtFf5ybne-4Mmc13SYJqY-WTAVuKWLuCN-aKJgMCY5BVX"}' http://localhost:8137/api/v1/open/share/verify   # 过期后
{"success":false,"token":"","passwordRequired":false,"message":"Failed","name":null}

$ curl -s -H "X-Docspell-Auth: <token>" http://localhost:8137/api/v1/sec/share/3qLtFf5ybne-4Mmc13SYJqY-WTAVuKWLuCN-aKJgMCY5BVX
{"id":"3qLtFf5ybne-4Mmc13SYJqY-WTAVuKWLuCN-aKJgMCY5BVX","query":"(& tag=invoice )","owner":{"id":"HD6Z6BDPQ3j-AYbimTV1yWF-mJUbw8UehDw-PKTWybegTo5","name":"admin"},"name":null,"enabled":true,"publishAt":1793540904229,"publishUntil":1793540924217,"expired":true,"password":false,"views":1,"lastAccess":1793540904268}

$ curl -s -o /dev/null -w "%{http_code}" -H "Docspell-Share-Auth: <share-token>" -H "Content-Type: application/json" -d '{"query":"","offset":0,"limit":20,"searchMode":"normal"}' http://localhost:8137/api/v1/share/search/query
403

5.7 admin 端点族

/api/v1/admin/* 用静态密钥头认证(secret 配置为空则整族禁用,配置见《Docspell 0.43.0 配置参数详解》)。实测对照:

$ curl -s -X POST http://localhost:8137/api/v1/admin/fts/reIndexAll   (无 secret 头)
HTTP/1.1 403 Forbidden
Date: Sun, 01 Nov 2026 12:44:13 GMT
Connection: keep-alive

$ curl -s -X POST -H "Docspell-Admin-Secret: q1w2e3r4" http://localhost:8137/api/v1/admin/fts/reIndexAll
{"success":true,"message":"Full-text index will be re-created."}

reIndexAll 重建所有集体的 Solr 索引(异步作业,进度看 5.2 queue state);集体级自助重建走 POST /sec/fts/reIndex(token 认证)。admin 族还有 resetPassword、collective/{id}/deletePreviewImages 等运维端点(168 路径全量以 /api/doc 活文档为准)。

5.8 integration 端点与目录监视

集成上传 POST /open/integration/item/{collectiveName}:给本机扫描枪、邮件网关等外部程序用的免登录通道,保护方式由配置选定(本部署 header 方式,值 q1w2e3r4)。实测三连:

$ curl -s -o /dev/null -w "%{http_code}" http://localhost:8137/api/v1/open/integration/item/admin   (GET 探测)
403
$ curl -s -X POST -H "Docspell-Integration: q1w2e3r4" -F file=@integration-note.txt http://localhost:8137/api/v1/open/integration/item/admin
{"success":true,"message":"Files submitted."}
$ curl -s -X POST -H "Docspell-Integration: wrong" -F file=@integration-note.txt http://localhost:8137/api/v1/open/integration/item/admin   (错误 secret)
403

GET 探测同样受保护(403),防止未授权方枚举集体名;带对 header 的 POST 成功,入库条目 source=integration(5.2 列表回显可见)。另有 GET /open/integration/checkfile/{collective}/{sha256} 供客户端查重预检。

目录监视是 integration 端点的官方配套 CLI(dsc):监视一个目录,新文件自动走集成上传。实测命令形态:

$ dsc -d http://docspell-restserver:7880 watch --delete -ir --not-matches "**/.*" --header "Docspell-Integration:q1w2e3r4" /opt/docs

两条实测规则:

规则一,文件必须放在 <监视根>/<集体名>/ 子目录下。直接放根目录,监视日志报:

Got: /opt/docs/consume4.txt
Watch - Error finding collective: The collective could not be deduced for /opt/docs/consume4.txt. Make sure there is a subdirectory with the name of the collective below /opt/docs

移入 admin/ 子目录后成功摄取(--delete 成功后删除源文件):

Got: /opt/docs/admin/consume4.txt
Adding to single request: /opt/docs/admin/consume4.txt
Sending request …
Deleted file
Server: Files submitted.

规则二,监视只响应新建/修改事件:把旧文件 mv 进集体子目录不会触发扫描(等待 40 秒无动静),touch 更新 mtime 立即入队:

$ touch import/admin/consume4.txt
$ curl -s -H "X-Docspell-Auth: <token>" "http://localhost:8137/api/v1/sec/item/search?q=&withDetails=true"   # 过滤新条目
{"id": "A3CDnrHPxJA-GqUfnmgYFBv-PzDgptSMnJT-8yeZEbtx7ym", "name": "consume4.txt", "state": "created", "date": 1793541730047, "dueDate": null, "source": "integration", "direction": "incoming", "corrOrg": null, "corrPerson": null, "concPerson": null, "concEquipment": null, "folder": null, "attachments": [{"id": "ECJmnFtD61i-DHoYPKxe1uD-LyzSm22vRLQ-h6WvhTHoGV5", "position": 0, "name": "consume4.txt", "pageCount": null}], "tags": [], "customfields": [], "relatedItems": [], "notes": null, "highlighting": []}

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

  • 分组:搜索结果按 groups[] 返回,组名是条目日期所在月份(实测 2026-11),组内按日期倒序;
  • 分页:offset + limit 平铺分页(跨组连续计数),limit 受服务端 max-item-page-size 封顶——超限请求返回封顶值并置 limitCapped: true(实测:默认 200;配置改 5 后 limit=50 请求回显 limit: 5 limitCapped: True,见《Docspell 0.43.0 配置参数详解》3.2);
  • 过滤:全部收敛在 q DSL(5.3),无独立 filter 参数;
  • 详略:withDetails=true 才返回附件/标签/自定义字段数组,默认精简响应;
  • 状态域:searchMode = normal(默认)/ trashed(回收站)/ all;
  • POST 等价:POST /sec/item/search 与 POST /share/search/query 用 ItemQuery JSON body,参数语义与 GET 完全一致。

七、常见问题

7.1 q 查询报 ParseFailure

裸词、含 - 的词(如文件名 d5-note)都会触发 Invalid query: ParseFailure(…)——查询 DSL 里 !/- 是取反、&/| 是组合符,裸文本不是合法谓词。按意图改写:按名字找条目用空 q 列表 + 客户端过滤(5.2 实测做法);按内容找用 content:词;含特殊字符的值加引号。

7.2 500 空响应体(字段不全)

sec/user、sec/source、sec/tag、sec/share 的创建类端点在请求体缺 required 字段时不返回 400/422,而是 500 + 空 body(实测均踩过,服务端日志分别是 DecodingFailure at : DownField(id)、Invalid search mode: simple、Failed to read input near 0 等)。排障口诀:500 空体 → 翻服务端日志找 DownField(字段名) → 对照 openapi schema 的 required 列表补齐。User 要 id/login/state/source/loginCount/created,Source 要 id/created,Tag 要 id/created。

7.3 logout 后 token 仍可用

无状态签名 token 的固有语义(4.4 实测 200)。脚本场景靠 validMs 自然到期控制风险;紧急吊销的唯一手段是轮换 auth.server-secret(全体掉线)。

7.4 share 检索 500:searchMode 取值

/share/search/query 的 searchMode 与 sec 检索不同源枚举,传 simple 报 500(日志 Invalid search mode: simple),合法值 normal/trashed/all(实测 26-07 用 normal 成功)。

7.5 目录监视文件不摄取

依次检查:① 是否在 <根>/<集体名>/ 子目录(5.8 规则一);② 是否只是 mv 了旧文件(需 touch,规则二);③ integration header 值与配置 integration-endpoint.http-header.header-value 是否一致(错值 403 静默拒收);④ --not-matches "**/.*" 是否把目标文件当隐藏文件排除。

7.6 上传成功但条目查不到内容

上传响应 Files submitted. 只代表入队(5.2);先查 GET /sec/queue/state 看作业 state,failed/stuck 看逐条 logs。FTS 查不到内容则确认 full-text-search 已启用且 Solr schema 已初始化(排障过程见《Docspell 0.43.0 配置参数详解》7.2)。

八、参考链接

Docspell 0.43.0 API 使用详解 - Quzuo