Papra 26.7.1 REST API 使用详解
Papra 26.7.1 REST API 使用详解:认证方式与 API Key 权限模型、组织/文档/标签/分享链接全域端点 curl 实测回显、错误码速查与分页过滤约定
Papra 26.7.1 REST API 使用详解
目标:讲清 Papra 26.7.1 的 REST API——认证方式、公开端点清单、核心域(组织/文档/标签/分享链接/API Key)的逐接口调用方法与实测响应回显、错误码速查与通用分页过滤约定。全部请求与回显均在 2026-10-31 于真实实例逐条实测采集,长响应标注节选。
一、环境信息
| 项 | 说明(实测环境) |
|---|---|
| 操作系统 | Ubuntu 22.04 |
| Papra | 26.7.1(PAPRA_VERSION=26.7.1,构建日期 2026-10-22) |
| 实例基址 | http://192.168.1.200:8134 |
| 演示账号 | admin@papra.local / q1w2e3r4 |
| 实验组织 | org_tqq4cqy4g6ui3iwvitpgucgh(首个注册账号自动创建的默认组织) |
| 客户端工具 | curl 7.81+ / python3 标准库 |
| 测试文档 | 设备验收报告(txt)、会议纪要(txt)、中文扫描件(png)、采购合同(pdf)等 5 份 |
💡 说明:回执中的令牌、ID 均来自本次实测实验实例(收工后已下线),仅作格式演示,不含可用凭据。
二、前置条件
2.1 端点与客户端工具
- API 前缀:
/api,全部为 REST over HTTP/JSON;与服务同端口同源(官方云端为https://api.papra.app/api,自托管即实例自身地址)。 - 客户端任选:curl(本文全部示例)、任意 HTTP 客户端、官方 SDK(TypeScript/JavaScript,见官方仓库
packages/)。 - 无需额外安装依赖,一个可用的 Papra 实例加 curl 即可复现本文全部操作。
2.2 创建 API Key(实测)
API Key 在界面"设置 → API 密钥"中创建,也可用登录会话调 POST /api/api-keys 创建(见 5.5 节)。创建时勾选权限(可全选),返回值中 token 字段仅此一次展示。
本文实测创建的全量权限 Key(名称 doc-test-key)与只读 Key(名称 ro-key,仅 tags:read,见 4.3)均已实际生成并用于后续验证。示例统一以环境变量引用:
export PAPRA_API_KEY='<你的 API Key token>'
export BASE=http://192.168.1.200:8134
export ORG=org_tqq4cqy4g6ui3iwvitpgucgh
三、API 总览
3.1 协议与版本
- 纯 REST:JSON 请求体(必须显式带
Content-Type: application/json,见 5.2 三个反例实测)+ 文件上传用multipart/form-data。 - URL 内实体 ID 带类型前缀与固定格式,不符合直接 400(不进入业务逻辑):
| 实体 | ID 格式(正则) | 示例 |
|---|---|---|
| 组织 | org_ + 24 位小写字母数字 | org_tqq4cqy4g6ui3iwvitpgucgh |
| 文档 | doc_ + 24 位小写字母数字 | doc_h0a5nz9a6kj6w4zz18cmcl1j |
| 标签 | tag_ + 24 位 | tag_b0m8vaz0ai0tch6jjaa8dvyn |
| API Key | ak_ + 24 位 | ak_bvu4nz6jhykpnmujnll6a95w |
| 分享链接(管理标识) | dsl_ + 24 位 | dsl_sl6agu1c0pkszq7ky5hlqqf4 |
| 分享链接(公开令牌) | 64 位字母数字(/^[a-z0-9]{64}$/i) | m63JZGZTMudE…(64 位) |
3.2 认证方式
两种认证并存,覆盖不同接口族:
| 方式 | 适用接口族 | 用法 |
|---|---|---|
| API Key(Bearer) | 业务 API(组织/文档/标签/自定义属性等) | 请求头 Authorization: Bearer <token> |
| 登录会话 | better-auth 认证端点(/api/auth/*)、API Key 管理(/api/api-keys)、恢复回收站文档等仅界面可用的操作 | 注册/登录后以 Cookie 会话维持 |
⚠️ 注意:权限不足的响应是 401 而非 403(实测,见 4.3);两类端点的错误体形态不同——业务 API 为
{"error":{"message","code"}},better-auth 端点为{"message","code"}(无外层error)。
3.3 官方公开端点清单(26.7.1,官方 API Endpoints 页 2026-10-31 查询)
| 域 | 端点 |
|---|---|
| API Key | GET /api/api-keys/current |
| 组织 | GET/POST /api/organizations、GET/PUT/DELETE /api/organizations/:organizationId |
| 文档 | POST /api/organizations/:organizationId/documents(上传)、GET …/documents(列表)、GET …/documents/deleted(回收站)、GET …/documents/:documentId、DELETE …/documents/:documentId、GET …/documents/:documentId/file、GET …/documents/statistics、PATCH …/documents/:documentId、POST …/documents/:documentId/reprocess、GET …/documents/:documentId/activity |
| 标签 | POST/GET /api/organizations/:organizationId/tags、PATCH/DELETE …/tags/:tagId、POST …/documents/:documentId/tags(打标)、DELETE …/documents/:documentId/tags/:tagId(摘标) |
| 打标规则 | POST …/tagging-rules/:tagRuleId/apply |
| 自定义属性 | …/custom-property-definitions 增删改查全套、…/documents/:documentId/custom-properties 读写清 |
💡 说明:实测发现 26.7.1 还有官方端点页未登记的接口族可用——分享链接(
POST …/documents/:documentId/share-links、GET /api/share-links/:token/document、POST /api/share-links/:token/verify、DELETE …/share-links/:shareLinkId,见 5.4)以及批量操作(documents/batch/tags、documents/batch/trash)等。官方页未列的接口属未承诺范围,升级时可能变动,生产调用建议以公开清单为界。
四、认证与首次调用
4.1 无令牌调用(实测 401)
curl -s -w '\nHTTP=%{http_code}\n' "${BASE}/api/organizations"
实测回显(HTTP 401):
{"error":{"message":"Unauthorized","code":"auth.unauthorized"}}
HTTP=401
令牌前缀错误(如去掉 ppapi_ 前缀或拼接多余空格)同样返回上述 401,服务端不区分"缺失"与"无效"。
4.2 首次调用:校验当前 Key(实测)
curl -s -w '\nHTTP=%{http_code}\n' -H "Authorization: Bearer ${PAPRA_API_KEY}" \
"${BASE}/api/api-keys/current"
实测回显(HTTP 200):
{"apiKey":{"id":"ak_bvu4nz6jhykpnmujnll6a95w","name":"doc-test-key","permissions":["organizations:read","organizations:create","documents:create","documents:read","documents:update","documents:delete","tags:create","tags:read","tags:update","tags:delete","custom-properties:read","custom-properties:create"]}}
4.3 API Key 权限模型(实测)
权限共 12 项(organizations/documents/tags/custom-properties 各域的 read/create/update/delete)。最小权限实测:用仅含 tags:read 的只读 Key 调删除文档接口:
curl -s -w '\nHTTP=%{http_code}\n' -X DELETE -H "Authorization: Bearer ${PAPRA_RO_KEY}" \
"${BASE}/api/organizations/${ORG}/documents/doc_h0a5nz9a6kj6w4zz18cmcl1j"
实测回显(HTTP 401):
{"error":{"message":"Unauthorized","code":"auth.unauthorized"}}
HTTP=401
⚠️ 注意:权限不足与未认证同样返回 401
auth.unauthorized,不是 403。排障时看到 401 先核对 Key 权限清单(GET /api/api-keys/current的permissions数组),再怀疑令牌本身。同一只读 Key 调GET …/tags正常返回 200,证明 401 确由权限缺失触发。
五、各 API 域详解
5.1 组织域(organizations)
列出组织(需 organizations:read):
curl -s -H "Authorization: Bearer ${PAPRA_API_KEY}" "${BASE}/api/organizations"
实测回显(HTTP 200):
{"organizations":[{"id":"org_tqq4cqy4g6ui3iwvitpgucgh","name":"默认组织","customerId":null,"deletedBy":null,"createdAt":"2026-10-31T01:58:56.988Z","updatedAt":"2026-10-31T01:58:56.988Z","deletedAt":null,"scheduledPurgeAt":null}]}
其余组织接口:POST /api/organizations({"name":"…"},3–50 字符)、GET /api/organizations/:organizationId、PUT /api/organizations/:organizationId(改名)、DELETE /api/organizations/:organizationId(软删,204;到期由定时任务清除,保留期见配置篇 5.11)。
5.2 文档域(documents)
上传文档(POST …/documents,multipart/form-data,文件放 file 字段;需 documents:create):
curl -s -w '\nHTTP=%{http_code}\n' -H "Authorization: Bearer ${PAPRA_API_KEY}" \
-F 'file=@/opt/papra-samples/设备验收报告-02.txt' \
"${BASE}/api/organizations/${ORG}/documents"
实测回显(HTTP 200,注意 content 为空——提取是异步的):
{"document":{"id":"doc_h0a5nz9a6kj6w4zz18cmcl1j","createdAt":"2026-10-31T02:48:24.606Z","updatedAt":"2026-10-31T02:48:24.608Z","organizationId":"org_tqq4cqy4g6ui3iwvitpgucgh","createdBy":"usr_v63ltj99aqbnbjq8jitdemkl","originalName":"设备验收报告-02.txt","originalSize":175,"originalSha256Hash":"b2f7b670c5c731819542fed25f84919a3fe48a4aab4770374548e622323eeb90","name":"设备验收报告-02.txt","mimeType":"text/plain","content":"","documentDate":null,"notes":null,"deletedAt":null,"deletedBy":null,"isDeleted":false}}
同内容去重:把同一文件改名再传,实测 HTTP 409:
{"error":{"message":"Document already exists.","code":"document.already_exists"}}
💡 说明:去重按
originalSha256Hash全组织比对,与文件名无关。这实现了官方 Architecture 页的 Document Deduplication 承诺(同内容只存一份原文件)。
获取文档详情(上传后稍等片刻回读,content 已被异步提取填充,HTTP 200):
{"document":{"id":"doc_h0a5nz9a6kj6w4zz18cmcl1j",…,"updatedAt":"2026-10-31T02:48:24.619Z","originalName":"设备验收报告-02.txt","originalSize":175,"originalSha256Hash":"b2f7b6…eeb90","name":"设备验收报告-02.txt","mimeType":"text/plain","content":"设备验收报告(YS-2026-042)\n\n设备名称:高速扫描仪 SC-50\n序列号:SN20261031A\n验收结论:通过,同意入库归档。\n抽检样本:30/30 合格。\n","documentDate":null,"notes":null,"deletedAt":null,"deletedBy":null,"isDeleted":false,"tags":[],"customProperties":[]}}
下载原文件(GET …/documents/:documentId/file,返回 application/octet-stream,内容与原件逐字节一致):
curl -s -H "Authorization: Bearer ${PAPRA_API_KEY}" \
"${BASE}/api/organizations/${ORG}/documents/doc_h0a5nz9a6kj6w4zz18cmcl1j/file"
实测回显(HTTP 200,响应体即原文):
设备验收报告(YS-2026-042)
设备名称:高速扫描仪 SC-50
序列号:SN20261031A
验收结论:通过,同意入库归档。
抽检样本:30/30 合格。
更新文档(PATCH …/documents/:documentId,需 documents:update;必须显式带 JSON 头):
curl -s -X PATCH -H "Authorization: Bearer ${PAPRA_API_KEY}" \
-H 'Content-Type: application/json' \
-d '{"name":"设备验收报告-YS-2026-042.txt","content":"设备验收报告 YS-2026-042:高速扫描仪 SC-50,序列号 SN20261031A,验收通过。"}' \
"${BASE}/api/organizations/${ORG}/documents/doc_h0a5nz9a6kj6w4zz18cmcl1j"
实测回显(HTTP 200,updatedAt 随之刷新):
{"document":{"id":"doc_h0a5nz9a6kj6w4zz18cmcl1j",…,"updatedAt":"2026-10-31T02:50:48.573Z","originalName":"设备验收报告-02.txt","name":"设备验收报告-YS-2026-042.txt","mimeType":"text/plain","content":"设备验收报告 YS-2026-042:高速扫描仪 SC-50,序列号 SN20261031A,验收通过。",…}}
缺 JSON 头的反例:同样的 PATCH 体不带 Content-Type: application/json(或写成 text/plain),实测 HTTP 400:
{"error":{"message":"Invalid request body","code":"server.invalid_request.body","details":[{"path":null,"message":"At least one of 'name', 'content', 'documentDate' or 'notes' must be provided"}]}}
错误细节极具迷惑性(提示"至少要提供一个字段",实际字段已带)——根因是请求体未被按 JSON 解析。所有 JSON 端点必须显式声明 Content-Type: application/json。
删除文档(进回收站)(DELETE …/documents/:documentId,需 documents:delete):
curl -s -o /dev/null -w '%{http_code}\n' -X DELETE -H "Authorization: Bearer ${PAPRA_API_KEY}" \
"${BASE}/api/organizations/${ORG}/documents/doc_h0a5nz9a6kj6w4zz18cmcl1j"
实测回显(HTTP 204,空响应体):
204
回收站列表(GET …/documents/deleted,isDeleted:true 并带删除人与删除时间):
{"documents":[{"id":"doc_h0a5nz9a6kj6w4zz18cmcl1j",…,"deletedAt":"2026-10-31T02:48:24.663Z","deletedBy":"usr_v63ltj99aqbnbjq8jitdemkl","isDeleted":true}],"documentsCount":1}
⚠️ 注意:回收站恢复(界面的"还原")在 26.7.1 不对 API Key 开放——全量权限 Key 调恢复操作实测仍返回 401
auth.unauthorized(恢复仅登录会话可用);超过保留期(默认 30 天)由定时任务硬删除,届时不可恢复。
组织文档统计(GET …/documents/statistics):
{"organizationStats":{"documentsCount":5,"documentsSize":236910,"deletedDocumentsCount":0,"deletedDocumentsSize":0,"totalDocumentsCount":5,"totalDocumentsSize":236910}}
ID 校验的两级错误(排障常用):
- 格式不合规(
doc_nonexistent123,不满足doc_[a-z0-9]{24})→ HTTP 400,根本不查库:
{"error":{"message":"Invalid URL parameters","code":"server.invalid_request.params","details":[{"path":"documentId","message":"Invalid format: Expected /^doc_[a-z0-9]{24}$/ but received \"doc_nonexistent123\""}]}}
- 格式合规但不存在 → HTTP 404:
{"error":{"message":"Document not found.","code":"document.not_found"}}
重新处理与活动记录:POST …/documents/:documentId/reprocess 对已入库文档按当前实例配置重新提取内容(默认限流 100 次/小时/组织,关闭时 403 document.reprocessing_disabled、超限 429 server.too_many_requests 并附 Retry-After;相关参数见配置篇 5.3);GET …/documents/:documentId/activity 返回文档操作时间线。
5.3 标签域(tags)
创建标签(POST …/tags,JSON 体):
curl -s -H "Authorization: Bearer ${PAPRA_API_KEY}" \
-H 'Content-Type: application/json' \
-d '{"name":"合同","color":"#2563EB","description":"采购与合同类文件"}' \
"${BASE}/api/organizations/${ORG}/tags"
实测回显(HTTP 200):
{"tag":{"id":"tag_b0m8vaz0ai0tch6jjaa8dvyn","createdAt":"2026-10-31T02:01:41.147Z","updatedAt":"2026-10-31T02:01:41.147Z","organizationId":"org_tqq4cqy4g6ui3iwvitpgucgh","name":"合同","normalizedName":"合同","color":"#2563EB","description":"采购与合同类文件"}}
⚠️ 注意:Content-Type 三态实测——官方 API 文档写"form-data",但 26.7.1 实际只接受
application/json:JSON 体返回 200,multipart/form-data与application/x-www-form-urlencoded均实测返回 HTTP 415:
{"error":{"message":"Unsupported Content-Type","code":"api.unsupported-content-type"}}
官方文档此处滞后,以实测为准。
normalizedName 字段做归一化(英文转小写):实测创建 测试A 后回读详情 normalizedName":"测试a"(同归一化名视为重复)。
给文档打标签(POST …/documents/:documentId/tags,JSON 体 {"tagId":"…"}):
curl -s -o /dev/null -w '%{http_code}\n' -X POST \
-H "Authorization: Bearer ${PAPRA_API_KEY}" -H 'Content-Type: application/json' \
-d '{"tagId":"tag_edh08smsg3u783yiz58tcxr7"}' \
"${BASE}/api/organizations/${ORG}/documents/doc_h0a5nz9a6kj6w4zz18cmcl1j/tags"
实测回显(HTTP 204);回读文档详情确认标签已挂载(tags 数组,长响应节选):
{"document":{…,"isDeleted":true,"tags":[{"id":"tag_edh08smsg3u783yiz58tcxr7","createdAt":"2026-10-31T02:49:54.799Z","updatedAt":"2026-10-31T02:49:54.799Z","organizationId":"org_tqq4cqy4g6ui3iwvitpgucgh","name":"测试A","normalizedName":"测试a","color":"#DC2626","description":null}],"customProperties":[]}}
标签列表(GET …/tags,含 documentsCount 使用计数):
{"tags":[{"id":"tag_b0m8vaz0ai0tch6jjaa8dvyn",…,"name":"合同","normalizedName":"合同","color":"#2563EB","description":"采购与合同类文件","documentsCount":1}]}
标签更新/删除(PATCH/DELETE …/tags/:tagId)与摘标(DELETE …/documents/:documentId/tags/:tagId)形态同上:成功 200/204。
5.4 分享链接域(share-links,官方端点页未登记)
创建分享链接(POST …/documents/:documentId/share-links):
带密码(JSON 体 {"password":"share1234","expiresAt":null})实测回显(HTTP 201):
{"shareLink":{"id":"dsl_sl6agu1c0pkszq7ky5hlqqf4","createdAt":"2026-10-31T02:50:48.621Z","updatedAt":"2026-10-31T02:50:48.621Z","organizationId":"org_tqq4cqy4g6ui3iwvitpgucgh","documentId":"doc_h0a5nz9a6kj6w4zz18cmcl1j","createdBy":"usr_v63ltj99aqbnbjq8jitdemkl","token":"m63JZGZTMudEREqSB8PWSacgw8r7eZT2ZkDv0fdso1wHn7p28rDeStnymlP7wa6l","expiresAt":null,"isEnabled":true,"lastAccessedAt":null,"isPasswordProtected":true,"url":"http://192.168.1.200:8134/share/m63JZGZTMudEREqSB8PWSacgw8r7eZT2ZkDv0fdso1wHn7p28rDeStnymlP7wa6l"}}
空 JSON 体 {} 实测创建无密码链接(HTTP 201,isPasswordProtected:false,其余结构同上)。
💡 说明:一条链接有两个标识,用途严格区分——
id(dsl_前缀)用于管理操作(删除、列表);token(64 位)用于公开访问(拼进/share/<token>页面地址与公开 API)。二者不可混用,混用即 400(见下)。
公开访问无密码链接(无需任何认证):
curl -s "${BASE}/api/share-links/${TOKEN64}/document"
实测回显(HTTP 200,元信息);GET /api/share-links/${TOKEN64}/document/file 同理返回 application/octet-stream 原文件:
{"document":{"name":"会议纪要.txt","size":481,"mimeType":"text/plain"}}
密码保护链接的验证流程(实测三步):
- 取受保护文档不带令牌 → HTTP 401:
{"error":{"message":"A valid access token is required to access this share link.","code":"share_link.password_required"}}
POST /api/share-links/:token/verify,JSON 体{"password":"share1234"}→ HTTP 200,签发短时 JWT(有效期默认 15 分钟,见配置篇 5.10):
{"accessToken":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzaGFyZUxpbmtJZCI6ImRzbF9zbDZhZ3UxYzBwa3N6cTdreTVobHFxZjQiLCJleHAiOjE3OTM0MTU5NDgsImlhdCI6MTc5MzQxNTA0OH0.4ezF07aHCopisN47IyaqtvKoPhFAwshx1wGVq_MdmXs"}
- 用标准 Bearer 头携带该 accessToken 再次公开访问 → HTTP 200 返回文档元信息与原文件:
curl -s -H "Authorization: Bearer ${SHARE_ACCESS_TOKEN}" \
"${BASE}/api/share-links/${TOKEN64}/document"
密码验证的错误分支(均实测):
| 请求 | 状态码 | 回显 |
|---|---|---|
| 密码错误 | 401 | {"error":{"message":"Invalid password.","code":"share_link.invalid_password"}} |
| verify 缺请求体 | 400 | {"message":"Expected \"password\" but received undefined",…} |
| verify 空密码 | 400 | {"message":"Invalid length: Expected >=1 but received 0",…} |
用 dsl_ 管理标识当 token 调 verify | 400 | {"error":{"message":"Invalid URL parameters","code":"server.invalid_request.params","details":[{"path":"shareLinkToken","message":"Invalid format: Expected /^[a-z0-9]{64}$/i but received \"dsl_gs46c0yj9sc900k8uqzmv3ra\""}]}} |
删除分享链接(管理操作,用 dsl_ 标识;HTTP 204):
curl -s -o /dev/null -w '%{http_code}\n' -X DELETE \
-H "Authorization: Bearer ${PAPRA_API_KEY}" \
"${BASE}/api/organizations/${ORG}/share-links/dsl_sl6agu1c0pkszq7ky5hlqqf4"
实测回显:
204
单文档链接数量上限、令牌时效与三条访问限流的参数化控制见配置篇 5.10。
5.5 API Key 管理域(仅登录会话可用)
API Key 的创建/列表/删除不开放给 API Key 自身,须用登录会话(POST /api/auth/sign-in/email 取 Cookie,回执见部署篇 4.5)。实测用会话创建只读 Key:
curl -s -b /tmp/papra-cookies.txt -H 'Content-Type: application/json' \
-d '{"name":"ro-key","permissions":["tags:read"]}' \
"${BASE}/api/api-keys"
实测回显(HTTP 200,token 仅此一次返回):
{"apiKey":{"id":"ak_wyjst8nhj1bwjh18y7igq93n","createdAt":"2026-10-31T02:50:48.765Z","updatedAt":"2026-10-31T02:50:48.765Z","name":"ro-key","keyHash":"wVc574qQCqaN0I5xKJY7Mt83O_tc7NVBY6Tp81ErB00","prefix":"ppapi_lE0oF","userId":"usr_v63ltj99aqbnbjq8jitdemkl","lastUsedAt":null,"expiresAt":null,"permissions":["tags:read"],"allOrganizations":true},"token":"ppapi_lE0oFJxiGmq6EB3NS3CYoRSIMlOlJqJHDk172r6qPFSVnGOQLMdx5FihmmiTHgUT"}
GET /api/api-keys 列出全部 Key(含 keyHash、prefix、lastUsedAt),DELETE /api/api-keys/:id 吊销(HTTP 204,实测吊销后该 Key 立即失效)。
5.6 自定义属性与打标规则(官方公开域)
- 自定义属性:
POST/GET …/custom-property-definitions定义(含类型),PATCH/DELETE …/custom-property-definitions/:id维护,GET …/documents/:documentId/custom-properties读取,POST/DELETE …/documents/:documentId/custom-properties/:definitionId写入/清除值。每组织定义数上限默认 100(配置篇 5.11)。 - 打标规则:
POST …/tagging-rules/:tagRuleId/apply把既有规则补打到存量文档(规则本身在界面维护)。自动打标的 AI 参数见配置篇 5.12。
六、分页/过滤/排序通用约定
GET …/documents 列表接口的通用查询参数(实测验证):
| 参数 | 默认 | 取值 | 实测要点 |
|---|---|---|---|
| pageIndex | 0 | 从 0 起的页序号 | 与 pageSize 配合翻页 |
| pageSize | 100 | 每页条数 | 响应只含当前页 + 总数 |
| searchQuery | — | 关键字或高级过滤表达式 | 同时命中 name 与提取后的 content(含 PDF OCR 文本):实测搜"扫描仪"同时命中会议纪要(正文提及)与采购合同(PDF 扫描内容提及)两份文档 |
| sortField | createdAt | createdAt / updatedAt / name / documentDate | 实测 sortField=name&sortOrder=asc 按名称正序返回 |
| sortOrder | desc | asc / desc | — |
响应统一为 {"documents":[…],"documentsCount":总数} 结构;回收站列表(GET …/documents/deleted)同构。
💡 说明:高级过滤表达式(
field:value组合等)语法见官方 Guides → Advanced Search;本文实测的关键字检索已覆盖 content 全文与 OCR 内容两条链路。
七、常见问题
Q1:请求返回 401,令牌明明没写错?
按顺序核对三点:① 是否带了 Authorization: Bearer 前缀(实测缺前缀即 401 auth.unauthorized);② 该 Key 的权限是否覆盖目标接口——权限不足也是 401,不是 403(实测仅 tags:read 的 Key 调删除文档返回 401);③ 恢复回收站文档等界面专属操作不接受 API Key,须用登录会话。
Q2:JSON 接口反复报 400 "At least one of … must be provided" 或 415?
请求体没有被按 JSON 解析。所有 JSON 端点必须显式带 Content-Type: application/json(实测 PATCH 缺头报 400 server.invalid_request.body、建标签用 form-data 报 415 api.unsupported-content-type)。官方 API 文档中建标签写 form-data 属滞后信息,以实测为准。
Q3:上传返回 409 Document already exists? 同组织内已存在相同 SHA256 内容的文档(改名无效)。确认是否真要重复归档;同一份内容本就只存一份(官方去重架构),也可给旧文档打标/补备注后复用。
Q4:URL 里的 ID 报 400 Invalid URL parameters?
ID 格式不满足对应正则(如文档必须 doc_ + 24 位小写字母数字)。先从列表接口取真实 ID,不要手拼。
Q5:上传成功但 content 一直是空?
内容提取是异步的:上传回执里 content 为空串,稍后回读详情即有值(实测 175 字节 txt 上传后 11 秒内完成,见 5.2 回显的 updatedAt 变化)。扫描件无内容时检查 OCR 语言配置(配置篇 5.3/5.5)与 DOCUMENTS_CONTENT_EXTRACTION_ENABLED 开关。
Q6:分享链接访问 401?
区分两种 401:share_link.password_required 是受保护链接未带 accessToken——走 verify 换 JWT 后以 Authorization: Bearer <accessToken> 访问(注意不是自定义头);share_link.invalid_password 是密码错误。另外 verify 只认 64 位 token,dsl_ 管理标识会直接 400。
Q7:better-auth 端点的错误为什么没有 error 包裹层?
两类端点错误形态不同:业务 API 为 {"error":{"message","code"}};/api/auth/* 等认证端点为 {"message","code"}(如 EMAIL_PASSWORD_SIGN_UP_DISABLED,实测回显见配置篇 3.3)。写统一错误处理时两类都要兼容。
八、参考链接
- 官方 API Endpoints 页:https://docs.papra.app/api-endpoints (2026-10-31 查询)
- 官方 API 认证说明:https://docs.papra.app/api-authentication (2026-10-31 查询)
- 官方高级检索指南:https://docs.papra.app/guides/advanced-search (2026-10-31 查询)
- 官方架构说明(去重/无变更原则):https://docs.papra.app/architecture/document-deduplication (2026-10-31 查询)
- 官方仓库与 SDK:https://github.com/papra-hq/papra (2026-10-31 查询)
本专栏姊妹篇:《Papra 26.7.1 介绍与选型指南》、《Papra 26.7.1 Docker Compose 部署指南》、《Papra 26.7.1 配置参数详解》。