カスタムソースで自社のシステムを同期する
カスタムソースは、組み込みのコネクターがないシステムをつなぎます。自社のWiki、チケット管理ツール、データベース、あらゆる場所からのエクスポートなどです。コードがドキュメントをSursaに送信すると、Sursaはそれを、ソースに設定されたコレクションとフォルダーのカードにします。カードには、他のソースのカードと同じ検索、回答、検証、権限が適用されます。
カードを直接書き込む場合と違い、カスタムソースは送信済みのものを記録します。完全同期の最後に、Sursaは送信されなかったすべてのドキュメントをアーカイブするので、自社のシステムでの削除が、追加の作業なしにSursaに反映されます。
ソースを設定する
管理者は、SursaのSources → New source → Custom source(ソース → 新しいソース → カスタムソース)でソースを作成し、次を選びます。
- カードを入れるコレクションと、必要に応じてフォルダー。
- フォルダーモード:選んだフォルダーにすべてのカードを入れるか、各ドキュメントの
pathを反映したサブフォルダーを作るか。 - 権限:コレクションの権限を使うか、各ドキュメントで参照できる人を指定する「ソースと同じ」にするか。
ソースのページにはそのIDが表示されます。例では42を使います。
キーを選ぶ
| キー | 先頭の文字 | できること |
|---|---|---|
| ソースキー | kbsrc_ | この1つのソースへのドキュメントの書き込み。それ以外は何もできず、読み込みも検索もできません。ソースのページのAPI keys(APIキー)で作成します。 |
import:writeスコープを持つ組織のキー | kb_ | 組織のすべてのカスタムソースへの書き込みと、その状態の読み取り。ソースとそのキーの作成にはadminスコープが必要です。 |
1つの連携システム、特に第三者のプラットフォームで動く連携システムには、ソースキーが最も安全です。複数のソースにデータを送るサービスには、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 | はい(ファイルがない場合) | 5MBまで。 |
content_type | いいえ | html(デフォルト)、markdown、text。HTMLからは、スクリプト、スタイル、フォーム、埋め込みオブジェクトが取り除かれます。 |
url | いいえ | 自社のシステムでのドキュメントへのリンク。カードに表示されます。 |
path | いいえ | フォルダー。配列(["Support", "Billing"])または文字列("Support/Billing")で、10階層まで。 |
permissions | いいえ | {"everyone": true}、または{"emails": [...], "groups": [...]}。ソースの権限が「ソースと同じ」の場合に使われます。 |
tags | いいえ | 50個までのタグ。ソースのタグに追加されます。 |
author_email | いいえ | 組織のユーザーであれば、カードの担当者になります。 |
updated_at | いいえ | 自社のシステムでドキュメントが変更された日時(RFC 3339)。 |
metadata | いいえ | カードに付ける独自のキーと値のペア。メタデータを参照してください。 |
category | いいえ | カードのカテゴリ。組織のカテゴリのキーで指定します。 |
verifiers、verify_interval_days | いいえ | カードを検証する人(メールアドレスで指定するユーザーとグループ)と、その頻度(日数)。 |
upload_id | いいえ | パートに分けたアップロードによるドキュメントのファイル。contentの代わりに使います。 |
attachments | いいえ | ドキュメントの追加のファイル(最大20個)。upload_idのリストで指定します。それぞれがソースの個別のカードになります。 |
1件のドキュメントなら、contentの代わりに、multipart/form-dataのリクエストで最大100MBのファイルを送ることもできます。ファイルはfileパートに、上のフィールドはJSONとしてdocumentパートに入れます。upload_idとattachmentsのためのパートに分けたアップロードは、import:writeスコープを持つ組織のキーで、用途import_attachmentを指定して作成します。ソースキーでは作成できません。
2つの送信方法
直接更新は、ヘルプデスクからのWebhookやZapierのステップのように、変更を1件ずつ知らせるシステムに向いています。ドキュメントが作成または変更されたら送信し、削除されたら削除します。
| リクエスト | 処理 |
|---|---|
POST /v1/push/sources/{source_id}/documents | 1件のドキュメントを作成または置換します。 |
POST /v1/push/sources/{source_id}/documents/batch | 多数のドキュメントを一度に作成または置換します。 |
DELETE /v1/push/sources/{source_id}/documents/{external_id} | 1件のドキュメントを削除します。IDはURLエンコードしてください。 |
変更は1分ほどでカードになります。続けて行われた複数の変更は、まとめて処理されます。
同期は、定期的なエクスポートに向いています。すべて、または変更されたものすべてを送信すると、Sursaが照合します。このガイドの残りでは、同期について説明します。
完全同期を実行する
同期は、開始、送信、終了の3つの手順からなります。
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が返ります。- 同時に開ける同期は1つだけです。2つ目を開こうとすると
409sources.push_sync_openが返ります。 - 同期は
max_minutes(デフォルトは60、最大1440)のあいだ開いたままになります。その後は期限切れになり、何もアーカイブされず、その番号は受け付けられなくなります。エクスポートにかかる時間より十分に長い時間を選んでください。
2. ドキュメントを送信
ドキュメントは、同期の番号を付けて、最大500件のバッチで送信します。バッチのボディは50MiBまでです。バッチの中のファイルは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(アトミックバッチの別のドキュメントが失敗した)。 |
デフォルトでは、バッチの各ドキュメントは個別に適用されます。1件の不正なドキュメントが他を止めることはありません。
前回の同期から変わっていないドキュメントは、もう一度送信する必要はありません。完全な照合でそれらが残るように、POST /v1/push/sources/{source_id}/syncs/{sync_number}/touchで、1回のリクエストで最大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は1分ほどでドキュメントをカードにします。ソースのページには、各同期が、送信されたドキュメントとアーカイブされたドキュメントの数とともに表示され、GET /v1/push/sources/{source_id}/syncs/{sync_number}も同じカウンターを返します。
増分同期
完全同期のあいだには、変更されたものだけを送信します。
"type": "incremental"で開始します。- 変更されたドキュメントを送信し、削除されたドキュメントのIDを
deletesに入れます(下記)。 complete_incrementalで終了します。
増分同期で漏れたものを拾うために、ときどき(たとえば毎週)完全同期を実行してください。
ドキュメントを一括で削除する
バッチでは、書き込むドキュメントと一緒にドキュメントを削除できます。そのIDをdeletesに並べます。
{
"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を送信すると、バッチは完全に適用されるか、まったく適用されないかのどちらかになります。どれか1件のドキュメントが確認に失敗すると、バッチは何も適用されず、レスポンスはコードsources.push_batch_rejectedとともに422になり、error.details.resultsにすべてのドキュメントの結果が含まれます。失敗したドキュメントにはerrorが、それ以外にはskippedが入ります。
{"sync_number": 8, "atomic": true, "documents": […], "deletes": ["faq-old-returns"]}
atomicがなければ、バッチはドキュメントごとに適用されます。関連する変更にはアトミックバッチを、1件の不正なドキュメントで残りを止めるべきではない大量のロードには通常のバッチを使ってください。
ドキュメントごとの結果を確認する
バッチのレスポンスは、各ドキュメントがどのように書き込まれたかを示します。カードになるのはその後なので、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のドキュメントが2枚目のカードになることはありません。結果はunchangedまたはupdatedになります。 - 同期の途中でプロセスが止まった場合は、古い同期の期限が切れてから新しい同期を開始するか、番号がわかっていれば古い同期を
failedで終了して、同期をもう一度実行してください。
上限
| 上限 | 値 |
|---|---|
| 1件のドキュメントの内容 | 5MB |
| 1件のドキュメントのリクエストでのファイル | 100MB |
| バッチあたりのドキュメント数 | 500 |
| バッチのリクエストボディ | 50MiB |
touchリクエストあたりのID数 | 1000 |
| ソースあたりのドキュメント数 | プランによる:100,000から |
| 開いている同期 | デフォルトで60分、最大24時間 |
ソースのドキュメント数の上限を超えたドキュメントは、バッチでは理由limit_reachedで失敗します。1件のドキュメントのリクエストには、コードsources.push_too_many_documentsとともに409が返ります。他のリクエストと同じくレート制限が適用されます。レート制限とクォータを参照してください。
よくあるエラー
| 応答 | 原因 | 対処 |
|---|---|---|
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の時間だけ待ってください。より大きなバッチで、送信の回数を減らしてください。 |