---
title: "同期のソースに一式を送る"
method: POST
path: "/knowledge/sources/{source_id}/sync"
tags: ["Sync"]
---

# 同期のソースに一式を送る

`POST /knowledge/sources/{source_id}/sync`

## Request

- Base URL: `https://api.leafchat.app/api/v1`
- URL: `https://api.leafchat.app/api/v1/knowledge/sources/{source_id}/sync`
- Auth: HTTP bearer

送った一式と、ソースの今の文書を external_id で突き合わせ、作成・更新・名前の変更・移動・削除を計画して実行する。一式は items か files のどちらか一方で送る。

files の項目は、テキスト（md・txt・json）なら本文（content）ごと、それ以外（html・csv・pdf・docx。テキストも可）は参照（sha256・size・content_type）で送る。参照の項目は計画の uploads_required に出たファイルを PUT …/uploads/{sha256} で上げてから実行する。足りないまま実行すると 409 upload_required になる。参照の項目の作成・更新は 1 回の実行で 10 件まで（残りは remaining）。

files で送ると、パスの最初の階層をフォルダ、最後をファイル名として扱う（2 階層まで）。leafchat に無いフォルダは、サイトごとにナレッジを分けていない組織なら実行のときに作り、分けている組織では 409 folder_not_found で止める。client には送り手の名前・版・CI・実行の URL を入れる。ダッシュボードのソースの画面に出る。

files で送ると、消える文書と現れる文書の中身の SHA-256 が一致し、組が 1 つに決まるものは、削除と作成ではなく名前の変更（rename）として扱う。文書 ID と版の履歴が続く。名前と中身を同時に変えると削除と作成になる。

dry_run が true なら計画だけを返し、文書を書かない（読み取りの用途の鍵でも使える）。用途「同期の確認」の鍵は、対象のソースの dry-run だけができる（PR の確認に使う）。warnings には、止めずに知らせる注意（別のフォルダの同じファイル名・Markdown の節の途中の切れ目）が入る。

実行は 1 回で最大 100 操作・25 秒前後（時間を使い切ると、新しい操作を始めずに残りを remaining に返す）。remaining が 0 になるまで同じ一式で呼び直す。最終同期のまとめ（ダッシュボードのソースの一覧）は、呼び出しをまたいだ同期の全体を数える。

一式に無い文書の削除は、確認なしで消せる数を超えると 409 delete_limit_exceeded になる。消してよい external_id を confirmed_deletes に入れる。

実行は API キーごとに 1 分 10 回まで（dry-run は数えない）。本文は 8 MiB まで。

変更点（files を足したとき）：external_id の規則を、日本語のパスを入れられる形（UTF-8 で 512 バイトまで・NFC・`/` 区切り）へ広げた。items の external_id と confirmed_deletes も同じ規則で受け、今まで通っていた ID はすべてそのまま通る。応答の folder_id・to_folder_id は、files で作るフォルダのときだけ null になる（items では常に文字列）。plan.rename と executed[].kind の rename は files で送ったときだけ出る（items では plan.rename は常に空）。ソースの last_sync_summary に renamed を足した。warnings の要素は code で形が分かれる（same_filename_across_folders・chunk_boundary）。

## Path parameters

- `source_id` string, required

## Request body

- SyncRequest
  - `items` object[]
    - `external_id` string, required
    - `filename` string, required
    - `content` string, required
    - `content_type` 'text/markdown' | 'text/plain', required
    - `folder_id` string
  - `files` union[]
    - union
      - SyncFileInline
        - `path` string, required: ソースの根からの相対パス（`/` 区切り・NFC）。最初の階層がフォルダ、最後がファイル名。2 階層まで
        - `external_id` string: 省くと path を外部 ID にする。送り手が変わらない ID を持つときだけ送る
        - `content` string, required
        - `content_type` 'text/markdown' | 'text/plain' | 'application/json', required
      - SyncFileRef
        - `path` string, required: ソースの根からの相対パス（`/` 区切り・NFC）。最初の階層がフォルダ、最後がファイル名。2 階層まで
        - `external_id` string: 省くと path を外部 ID にする。送り手が変わらない ID を持つときだけ送る
        - `sha256` string, required: ファイルの中身の SHA-256（16 進の小文字）
        - `size` integer, required: バイト数。形式ごとの上限（テキスト 4 MiB・その他 15 MiB）まで
        - `content_type` 'text/plain' | 'text/markdown' | 'application/json' | 'text/html' | 'text/csv' | 'application/pdf' | 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', required
  - `confirmed_deletes` string[]
  - `allow_empty` boolean
  - `dry_run` boolean
  - `client` SyncClient
    - `name` string, required
    - `version` string, required
    - `ci` 'github_actions' | 'other'
    - `repository` string
    - `run_url` string, uri: 同期を実行した CI の実行の URL（https だけ）

## Response `200`

計画と、実行した操作

- SyncResponse
  - `dry_run` boolean, required
  - `plan` object, required
    - `folders_to_create` object[], required
      - `name` string, required
      - `similar_to` string, nullable, required: 空白・大文字小文字・全角半角だけが違う既存のフォルダの名前（打ち間違いの手がかり）
    - `create` object[], required
      - `external_id` string, required
      - `filename` string, required
      - `parts` integer, nullable, required: 参照の形（sha256）で送った項目は、中身を受け取る前なので null
      - `folder_id` string, nullable, required: この同期で作るフォルダなら null
      - `folder_name` string, required
    - `update` object[], required
      - `external_id` string, required
      - `document_id` string, required
      - `filename` string, required
      - `parts` integer, nullable, required
      - `to_folder_id` string, nullable, required
      - `to_folder_name` string, nullable, required
    - `move` object[], required
      - `external_id` string, required
      - `document_id` string, required
      - `to_folder_id` string, nullable, required: この同期で作るフォルダなら null
      - `to_folder_name` string, required
    - `rename` object[], required: 消える文書と現れる文書の中身の SHA-256 が一致し、組が 1 つに決まったもの。文書 ID と版の履歴が続く（items では常に空）
      - `from_external_id` string, required: 今の external_id（前回のパス）
      - `external_id` string, required: 新しい external_id（今回のパス）
      - `document_id` string, required
      - `filename` string, required
      - `to_folder_id` string, nullable, required: フォルダが変わらないか、この同期で作るフォルダなら null
      - `to_folder_name` string, nullable, required: フォルダが変わらなければ null
    - `delete` object[], required
      - `external_id` string, required
      - `document_id` string, required
      - `filename` string, required
    - `unchanged` string[], required
    - `unmanaged` string[], required
  - `limits` object, required
    - `delete_cap` integer, required
    - `max_files` integer, required
    - `source_count` integer, required
    - `total_count` integer, required
  - `uploads_required` UploadNeed[], required: まだ受け取っていないファイル。PUT …/uploads/{sha256} で上げてから実行の同期を呼ぶ
    - `sha256` string, required
    - `size` integer, required
    - `content_type` string, required
    - `external_ids` string[], required
  - `warnings` union[], required
    - union
      - object
        - `code` 'same_filename_across_folders', required
        - `external_ids` string[], required
        - `filename` string, required
      - object
        - `code` 'chunk_boundary', required
        - `external_id` string, required
        - `part` integer, required: 見出しから始まらないパートの番号（2 から）
        - `line` integer, required: そのパートが始まる、本文の中の行（1 から）
  - `created_folders` object[], required
    - `name` string, required
    - `folder_id` string, required
  - `executed` object[], required
    - `kind` 'create' | 'update' | 'rename' | 'move' | 'delete', required
    - `external_id` string, required
    - `document_id` string, required
    - `revision_id` string, nullable, required
  - `remaining` integer, required: まだ実行していない操作の数。0 になるまで同じ一式で呼び直す

## Other responses

- `400`: 要求の形式か値が正しくない。errors（検証の誤り）に項目（path）ごとの理由が入る 返る code：`invalid_request`・`empty_manifest`・`duplicate_item`
- `401`: API キーが無いか無効（期限切れ・失効・削除の途中の組織を含み、理由は区別しない） 返る code：`unauthorized`
- `403`: この API キーの用途か、対象のソースでは実行できない 返る code：`forbidden`・`needs_draft`
- `404`: 指定した ID かパスが無い（ほかの組織の ID も同じ応答になる）。削除した文書は、削除の起票が保守の GC で消えるまで DELETE が already_deleted を返し、その後は 404 返る code：`not_found`
- `409`: 今の状態と合わない。code で見分ける 返る code：`source_locked`・`limit_exceeded`・`delete_limit_exceeded`・`folder_not_found`・`folder_limit_exceeded`・`upload_required`・`upload_not_expected`・`sync_in_progress`・`not_sync_source`・`same_name_exists`・`external_id_taken`・`draft_limit_exceeded`・`already_decided`・`not_retryable`
- `413`: 本文が上限を超えた（JSON 6 MiB・multipart 16 MiB・同期 8 MiB・同期のアップロード 15 MiB）。テキストの本文が 4 MiB を超えたときは、本文の上限の内側でも 422 invalid_request になる 返る code：`payload_too_large`
- `415`: Content-Type が、その操作の受け付ける形式でない。文書の作成に application/x-www-form-urlencoded で送ると、415 ではなく 400 になる 返る code：`unsupported_media_type`
- `422`: 内容を受け付けられない（編集の対象・分割の上限・形式・同期の項目・アップロードの中身など）。code で見分ける 返る code：`invalid_request`・`edit_no_match`・`edit_ambiguous`・`not_editable`・`too_many_parts`・`invalid_item`・`upload_mismatch`
- `429`: 上限を超えた。Retry-After の秒数だけ待ってからやり直す。上限は、API キーごとに 1 分 120 回（REST と MCP で共通）・同期の実行は鍵ごとに 1 分 10 回（dry_run は数えない）・組織の文書の書き込みは 1 分 300 版・API キーの検証の失敗は IP ごとに 1 分 20 回（超えると、正しい鍵でも 429 になる）。このほか、IP ごとの上限を超えると Cloudflare が 429（error code 1015）を返す。この応答は problem+json でないことがあり、Retry-After が無ければ少し待ってからやり直す 返る code：`rate_limited`
- `500`: サーバーで問題が起きた。少し待ってからやり直す 返る code：`internal_error`
- `502`: 同期の途中で操作が失敗した。同じ一式で呼び直すと、済んだ操作を除いて続きから実行する 返る code：`operation_failed`

---

[API](https://skmtc.dev/leafchat/apis/leafchat-api.md) · [All operations](https://skmtc.dev/leafchat/apis/leafchat-api/llms.txt) · [OpenAPI document](https://skmtc.dev/leafchat/apis/leafchat-api/revisions/2667db05c2ff?raw)
