跳到主要内容
Sursa Developers

用自定义数据源同步您的系统

自定义数据源用于连接没有内置连接器的系统:您的 wiki、工单系统、数据库,或任何地方导出的数据。您的代码把文档推送到 Sursa,Sursa 把它们转换为卡片,放入数据源所设置的合集和文件夹中。这些卡片与来自其他数据源的卡片一样,支持搜索、回答、核实和权限控制。

与直接写入卡片不同,自定义数据源会记录您发送过的内容。全量同步结束时,Sursa 会归档所有您未发送的文档,因此您的系统中的删除操作无需额外工作就能同步到 Sursa。

设置数据源

管理员在 Sursa 的 Sources → New source → Custom source(数据源 → 新建数据源 → 自定义数据源)中创建数据源,并选择:

  • 卡片所在的合集,以及可选的文件夹;
  • 文件夹模式:所有卡片都放在所选文件夹中,或者按每个文档的 path 建立对应的子文件夹;
  • 权限:沿用合集的权限,或者“as in the source”(与数据源一致),即由每个文档列出谁可以查看它。

数据源页面显示数据源的 ID;示例中使用 42。

选择密钥

密钥开头能做什么
数据源密钥kbsrc_向这一个数据源写入文档,除此之外什么都不能做:不能读取,也不能搜索。在数据源页面的 API keys(API 密钥)区块中创建。
具有 import:write 权限范围的组织密钥kb_向组织的任何自定义数据源写入,并读取其状态。创建数据源及其密钥需要 admin 权限范围。

对于单个集成,尤其是运行在第三方平台上的集成,数据源密钥是最安全的选择。具有 import:write 的组织密钥适合向多个数据源提供数据的服务。

两种密钥的传入方式相同,所有推送接口都位于 /v1/push/sources/{source_id} 下。用 GET /v1/push/sources/{source_id} 检查密钥和数据源的状态:

curl https://api.sursa.com/v1/push/sources/42 \
  -H "Authorization: Bearer $SURSA_SOURCE_KEY"
{"source_id": 42, "name": "Helpdesk articles", "documents": 1840, "open_sync": null}

文档

字段必填说明
external_id是文档在您系统中的稳定 ID,最多 512 个字符。再次发送同一个 ID 会替换该文档。
title是卡片标题,最多 500 个字符。对于文件,默认为文件名。
content是,有文件时除外最多 5 MB。
content_type否html(默认)、markdown 或 text。HTML 中的脚本、样式、表单和内嵌对象会被清除。
url否文档在您系统中的链接,显示在卡片上。
path否文件夹,可以是数组(["Support", "Billing"])或字符串("Support/Billing"),最多 10 层。
permissions否{"everyone": true},或 {"emails": [...], "groups": [...]}。数据源的权限设置为“as in the source”时使用。
tags否最多 50 个标签,添加到数据源的标签之外。
author_email否如果是组织中的用户,该用户成为卡片的负责人。
updated_at否文档在您系统中的更改时间(RFC 3339)。
metadata否卡片上由您定义的键值对。参见元数据。
category否卡片的分类:组织中某个分类的键。
verifiers、verify_interval_days否谁来核实卡片(按电子邮件指定的用户和用户组),以及核实周期(天)。
upload_id否通过分段上传得到的文档文件,用于代替 content。
attachments否文档的其他文件,最多 20 个,是 upload_id 的列表。每个文件都会成为该数据源中单独的一张卡片。

单个文档可以不传 content,改为在 multipart/form-data 请求中附带一个最大 100 MB 的文件:文件放在 file 部分,上述字段以 JSON 形式放在 document 部分。upload_id 和 attachments 所用的分段上传须使用具有 import:write 权限范围的组织密钥创建,用途为 import_attachment;数据源密钥不能创建这些上传。

两种推送方式

直接更新适合会通知每次变更的系统,例如服务台的 Webhook 或 Zapier 中的一个步骤。文档创建或更改时发送它,被删除时删除它:

请求作用
POST /v1/push/sources/{source_id}/documents创建或替换一个文档。
POST /v1/push/sources/{source_id}/documents/batch一次创建或替换多个文档。
DELETE /v1/push/sources/{source_id}/documents/{external_id}删除一个文档。请对 ID 做 URL 编码。

变更在大约一分钟内转换为卡片;连续的多次变更会合并处理。

同步适合定期导出:发送全部内容或所有变更的内容,由 Sursa 进行比对。本指南的其余部分介绍同步。

运行全量同步

同步分三步:打开、推送、关闭。

1. 打开同步

POST /v1/push/sources/{source_id}/syncs 打开一次同步并返回其编号:

curl -X POST https://api.sursa.com/v1/push/sources/42/syncs \
  -H "Authorization: Bearer $SURSA_SOURCE_KEY" \
  -H "Idempotency-Key: sync-2026-10-24" \
  -H "Content-Type: application/json" \
  -d '{"type": "initial", "max_minutes": 180}'
{"sync_number": 7, "type": "initial", "status": "open", "opened_at": "2026-10-24T02:00:00Z",
 "expires_at": "2026-10-24T05:00:00Z", "pushed": 0, "removed": 0}
  • 数据源的第一次同步,type 为 initial;完成过一次同步后为 incremental。在完成任何同步之前发起 incremental 同步,会返回 sources.push_initial_required。
  • 同一时间只能打开一个同步:再打开一个会返回 409 sources.push_sync_open。
  • 同步保持打开 max_minutes 分钟(默认 60,最多 1440)。之后同步过期:不会归档任何内容,其编号也不再被接受。请选择一个比导出所需时间宽裕得多的时长。

2. 推送文档

按批次发送文档,每批最多 500 个,并附上同步编号。批次的请求体最大 50 MiB;批次中的文件以 upload_id 传入。

curl -X POST https://api.sursa.com/v1/push/sources/42/documents/batch \
  -H "Authorization: Bearer $SURSA_SOURCE_KEY" \
  -H "Idempotency-Key: sync-7-batch-1" \
  -H "Content-Type: application/json" \
  -d '{
    "sync_number": 7,
    "documents": [
      {"external_id": "faq-shipping", "title": "Shipping", "content_type": "markdown",
       "content": "# Shipping\n\nWe ship worldwide within 5 days.", "path": ["Support"]},
      {"external_id": "faq-refunds", "title": "Refunds", "content_type": "html",
       "content": "<p>Refunds take 14 days.</p>", "path": ["Support", "Billing"],
       "permissions": {"groups": ["Support"]}, "metadata": {"article_id": "1042"}}
    ]
  }'

响应按发送顺序为每个文档给出一个结果:

{
  "results": [
    {"external_id": "faq-shipping", "result": "unchanged", "status": "indexed", "card_id": "8c1d…", "version": "5e0f…"},
    {"external_id": "faq-refunds", "result": "updated", "status": "pending", "card_id": "3f2c…", "version": "a91b…"}
  ]
}
字段含义
result本次对文档做了什么:created、updated、unchanged、deleted、not_found、error 或 skipped(在未应用的原子批次中)。
status文档转换为卡片的进度:pending、processing(卡片已写入,正在索引)、indexed、failed 或 removed。
card_id文档对应的卡片(卡片生成后才有)。
version内容和字段的哈希值。再次发送的同一个文档版本相同,结果为 unchanged。
error、reason文档失败的原因。reason 是稳定的代码:content_invalid、file_rejected、too_large、limit_reached、index_failed 或 batch_rejected(原子批次中的另一个文档失败)。

默认情况下,批次中的每个文档单独应用:一个有问题的文档不会影响其他文档。

自上次同步以来没有变化的文档不必重新发送。用 POST /v1/push/sources/{source_id}/syncs/{sync_number}/touch 按 ID 确认这些文档(每个请求最多 1000 个 ID),这样全量比对时会保留它们:

curl -X POST https://api.sursa.com/v1/push/sources/42/syncs/7/touch \
  -H "Authorization: Bearer $SURSA_SOURCE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"external_ids": ["faq-contact", "faq-hours"]}'

3. 关闭同步

POST /v1/push/sources/{source_id}/syncs/{sync_number}/close 结束同步:

curl -X POST https://api.sursa.com/v1/push/sources/42/syncs/7/close \
  -H "Authorization: Bearer $SURSA_SOURCE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status": "complete"}'
status效果
complete全量比对。数据源中本次同步既未推送也未确认的所有文档都会被归档。只有在发送了全部内容时才使用。
complete_incremental应用已发送的内容,不归档任何文档。
failed不归档任何文档,保留已推送的内容。可以加上 reason(例如 API_ERROR)和 error_details,写入日志。

导出中途停止时,一定要以 failed 关闭:部分导出之后用 complete 关闭,会归档所有还没来得及发送的文档。重复同样的关闭请求会返回同样的结果。

关闭后,Sursa 在大约一分钟内把文档转换为卡片。数据源页面显示每次同步及其推送和归档的文档数量,GET /v1/push/sources/{source_id}/syncs/{sync_number} 返回同样的计数。

增量同步

在两次全量同步之间,只发送变更的内容:

  1. 以 "type": "incremental" 打开同步。
  2. 推送变更的文档,并把已删除文档的 ID 放进 deletes(见下文)。
  3. 以 complete_incremental 关闭同步。

请定期(例如每周)运行一次全量同步,补上增量同步可能遗漏的内容。

在批次中删除文档

批次可以在写入文档的同时删除文档。在 deletes 中列出要删除的 ID:

{
  "sync_number": 8,
  "documents": [{"external_id": "faq-shipping", "title": "Shipping", "content": "…"}],
  "deletes": ["faq-old-returns", "faq-fax"]
}

每个被删除的 ID 在 results 中都有单独的一项,result 为 deleted;如果数据源中没有该文档,则为 not_found。再次删除同一个 ID 不会造成影响。

原子批次

有些变更只有一起应用才有意义,例如一个文档和替换它的文档。传入 "atomic": true 后,批次要么全部应用,要么完全不应用:只要有一个文档未通过检查,整个批次都不会应用,响应为 422,错误码为 sources.push_batch_rejected,每个文档的结果在 error.details.results 中:失败的文档带有 error,其他文档为 skipped。

{"sync_number": 8, "atomic": true, "documents": […], "deletes": ["faq-old-returns"]}

不带 atomic 时,批次逐个文档应用。相关联的变更请使用原子批次;批量加载请使用普通批次,以免一个有问题的文档拖累其余文档。

检查每个文档的结果

批次的响应说明每个文档是如何写入的。转换为卡片是之后进行的,因此 status 可能仍为 pending。要找出某次同步中未能转换为卡片的文档:

curl "https://api.sursa.com/v1/push/sources/42/syncs/7/documents?status=failed" \
  -H "Authorization: Bearer $SURSA_SOURCE_KEY"

每一项包含 external_id、title、status、card_id、version 和 reason,列表用 cursor 分页。status 还可以取 pending、processing、indexed 和 removed。

也可以不主动查询,而是等待通知:事件 import.sync_completed(同步已关闭并处理完毕,附带总数)和 import.document_failed(文档未能转换为卡片,附带 reason)会通过 Webhook 投递,也会出现在事件记录中。

重试

  • 每个 POST 请求都传入 Idempotency-Key。使用同一个键重复发送的批次返回第一次的结果。
  • 即使不带该键,external_id 相同的文档也绝不会生成第二张卡片:结果为 unchanged 或 updated。
  • 如果进程在同步中途停止,请在旧同步过期后打开新同步;如果还记得旧同步的编号,也可以以 failed 关闭它。然后重新运行同步。

限制

限制项值
单个文档的内容5 MB
单文档请求中的文件100 MB
每批文档数500
批次请求体50 MiB
每个 touch 请求的 ID 数1000
每个数据源的文档数取决于套餐:100,000 起
打开的同步默认 60 分钟,最长 24 小时

在批次中,超出数据源文档数限制的文档会失败,原因为 limit_reached;单个文档的请求则返回 409,错误码为 sources.push_too_many_documents。速率限制与其他请求一样适用;参见速率限制与配额。

常见错误

响应原因处理方法
401 sources.push_key_invalid密钥有误、已撤销或属于其他数据源。检查密钥和路径中的数据源 ID。
400 sources.push_document_invalid缺少必填字段或超出限制。修正文档;在普通批次中,只有该文档会失败。
409 sources.push_sync_open另一个同步仍处于打开状态。关闭它,或等待它过期。
409 sources.push_sync_closed同步已关闭或已过期。打开新的同步;导出耗时较长时调大 max_minutes。
403 sources.feature_disabled组织的套餐不包含数据源功能。联系管理员。
429请求过多。等待 Retry-After;减少批次数量,增大每批的规模。