ซิงก์ระบบของคุณด้วยแหล่งข้อมูลแบบกำหนดเอง
แหล่งข้อมูลแบบกำหนดเอง เชื่อมต่อระบบที่ไม่มีคอนเนกเตอร์ในตัว เช่น วิกิ ระบบจัดการทิกเก็ต ฐานข้อมูล หรือข้อมูลที่ส่งออกมาจากที่ใดก็ได้ โค้ดของคุณส่งเอกสารไปยัง Sursa แล้ว Sursa จะเปลี่ยนเอกสารเหล่านั้นเป็นการ์ดในคอลเลกชันและโฟลเดอร์ที่ตั้งค่าไว้สำหรับแหล่งข้อมูลนั้น โดยมีการค้นหา คำตอบ การตรวจสอบ และสิทธิ์เหมือนการ์ดจากแหล่งข้อมูลอื่นทุกประการ
แหล่งข้อมูลแบบกำหนดเองต่างจาก การเขียนการ์ดโดยตรง ตรงที่ติดตามว่าคุณส่งอะไรไปแล้วบ้าง เมื่อจบการซิงก์แบบเต็ม Sursa จะเก็บเอกสารทุกฉบับที่คุณไม่ได้ส่งเข้าคลัง การลบในระบบของคุณจึงไปถึง Sursa โดยไม่ต้องทำอะไรเพิ่ม
ตั้งค่าแหล่งข้อมูล
ผู้ดูแลระบบสร้างแหล่งข้อมูลใน Sursa ที่ Sources → New source → Custom source (แหล่งข้อมูล → แหล่งข้อมูลใหม่ → แหล่งข้อมูลแบบกำหนดเอง) และเลือก:
- คอลเลกชัน และโฟลเดอร์สำหรับการ์ดของแหล่งข้อมูล ซึ่งจะระบุหรือไม่ก็ได้
- โหมดโฟลเดอร์ คือให้การ์ดทั้งหมดอยู่ในโฟลเดอร์ที่เลือก หรือให้มีโฟลเดอร์ย่อยตาม
pathของเอกสารแต่ละฉบับ - สิทธิ์ คือใช้สิทธิ์ของคอลเลกชัน หรือ “ตามแหล่งข้อมูล” ซึ่งเอกสารแต่ละฉบับระบุว่าใครมองเห็นได้บ้าง
หน้าแหล่งข้อมูลแสดง ID ของแหล่งข้อมูล ตัวอย่างใช้ 42
เลือกคีย์
| คีย์ | ขึ้นต้นด้วย | ทำอะไรได้ |
|---|---|---|
| คีย์แหล่งข้อมูล | kbsrc_ | เขียนเอกสารลงในแหล่งข้อมูลนี้แหล่งเดียว ไม่มีสิทธิ์อื่น อ่านหรือค้นหาไม่ได้ สร้างได้ในหน้าแหล่งข้อมูลที่ส่วน API keys (คีย์ API) |
คีย์ขององค์กรที่มีขอบเขตสิทธิ์ import:write | kb_ | เขียนลงในแหล่งข้อมูลแบบกำหนดเองใดก็ได้ขององค์กรและอ่านสถานะของแหล่งข้อมูลนั้น การสร้างแหล่งข้อมูลและคีย์ของแหล่งข้อมูลต้องใช้ขอบเขตสิทธิ์ admin |
คีย์แหล่งข้อมูลเป็นตัวเลือกที่ปลอดภัยที่สุดสำหรับระบบที่เชื่อมต่อระบบเดียว โดยเฉพาะระบบที่ทำงานบนแพลตฟอร์มของบุคคลภายนอก ส่วนคีย์ขององค์กรที่มี import:write เหมาะกับบริการที่ส่งข้อมูลให้หลายแหล่งข้อมูล
คีย์ทั้งสองแบบส่งด้วยวิธีเดียวกัน และเอนด์พอยต์สำหรับส่ง (push) ทุกรายการอยู่ภายใต้ /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": [...]} ใช้เมื่อสิทธิ์ของแหล่งข้อมูลตั้งเป็น “ตามแหล่งข้อมูล” |
tags | ไม่ | แท็กไม่เกิน 50 รายการ ซึ่งเพิ่มต่อจากแท็กของแหล่งข้อมูล |
author_email | ไม่ | หากเป็นผู้ใช้ขององค์กร ผู้ใช้นั้นจะเป็นผู้รับผิดชอบการ์ด |
updated_at | ไม่ | เวลาที่เอกสารเปลี่ยนแปลงในระบบของคุณ (RFC 3339) |
metadata | ไม่ | คู่คีย์-ค่าของคุณบนการ์ด ดู ข้อมูลเมตา |
category | ไม่ | หมวดหมู่ของการ์ด คือคีย์ของหมวดหมู่หนึ่งขององค์กร |
verifiers และ verify_interval_days | ไม่ | ผู้ตรวจสอบการ์ด (ผู้ใช้ตามอีเมลและกลุ่ม) และความถี่ในการตรวจสอบเป็นจำนวนวัน |
upload_id | ไม่ | ไฟล์ของเอกสารจาก การอัปโหลดเป็นส่วน แทน content |
attachments | ไม่ | ไฟล์เพิ่มเติมของเอกสารไม่เกิน 20 ไฟล์ ในรูปรายการ upload_id แต่ละไฟล์จะกลายเป็นการ์ดแยกของแหล่งข้อมูล |
แทนที่จะใช้ content เอกสารฉบับเดียวส่งไฟล์ขนาดไม่เกิน 100 MB ในคำขอ multipart/form-data ได้ โดยใส่ไฟล์ในส่วน file และฟิลด์ข้างต้นเป็น JSON ในส่วน document การอัปโหลดเป็นส่วนสำหรับ upload_id และ attachments ต้องสร้างด้วยคีย์ขององค์กรที่มีขอบเขตสิทธิ์ import:write และวัตถุประสงค์ import_attachment คีย์แหล่งข้อมูลสร้างการอัปโหลดเหล่านี้ไม่ได้
สองวิธีในการส่ง
การอัปเดตโดยตรง เหมาะกับระบบที่แจ้งคุณทุกครั้งที่มีการเปลี่ยนแปลง เช่น เว็บฮุกจากระบบช่วยเหลือลูกค้า หรือขั้นตอนใน 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} | นำเอกสารหนึ่งฉบับออก เข้ารหัส URL ของ ID ด้วย |
การเปลี่ยนแปลงจะกลายเป็นการ์ดภายในประมาณ 1 นาที การเปลี่ยนแปลงหลายรายการที่ต่อเนื่องกันจะประมวลผลไปพร้อมกัน
การซิงก์ เหมาะกับการส่งออกเป็นประจำ คุณส่งทุกอย่างหรือทุกอย่างที่เปลี่ยนแปลง แล้ว 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- เปิดการซิงก์ได้ครั้งละหนึ่งรายการเท่านั้น การเปิดรายการที่สองจะได้
409sources.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 (เอกสารอื่นในชุดแบบอะตอมิกล้มเหลว) |
โดยค่าเริ่มต้น เอกสารแต่ละฉบับในชุดจะถูกนำไปใช้แยกกัน เอกสารที่ผิดพลาดหนึ่งฉบับจึงไม่หยุดฉบับอื่น
เอกสารที่ไม่เปลี่ยนแปลงตั้งแต่การซิงก์ครั้งก่อนไม่ต้องส่งซ้ำ ให้ยืนยันเอกสารเหล่านั้นด้วย ID ผ่าน POST /v1/push/sources/{source_id}/syncs/{sync_number}/touch ได้ไม่เกิน 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 แล้วชุดนั้นจะถูกนำไปใช้ทั้งหมดหรือไม่ใช้เลย หากมีเอกสารใดไม่ผ่านการตรวจ จะไม่มีส่วนใดของชุดถูกนำไปใช้ และการตอบกลับจะเป็น 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) มาถึงทาง เว็บฮุก และใน ฟีดอีเวนต์
การลองใหม่
- ส่ง
Idempotency-KeyไปกับทุกคำขอPOSTชุดที่ส่งซ้ำด้วยคีย์เดิมจะได้ผลลัพธ์แรก - แม้ไม่มีคีย์ เอกสารที่มี
external_idเดิมก็จะไม่กลายเป็นการ์ดใบที่สอง แต่จะได้unchangedหรือupdated - หากโปรเซสหยุดกลางการซิงก์ ให้เปิดการซิงก์ใหม่หลังการซิงก์เดิมหมดอายุ หรือปิดการซิงก์เดิมด้วย
failedหากยังมีหมายเลขอยู่ แล้วรันการซิงก์อีกครั้ง
ขีดจำกัด
| ขีดจำกัด | ค่า |
|---|---|
| เนื้อหาของเอกสารหนึ่งฉบับ | 5 MB |
| ไฟล์ในคำขอแบบเอกสารเดียว | 100 MB |
| จำนวนเอกสารต่อชุด | 500 |
| เนื้อหาคำขอของชุด | 50 MiB |
จำนวน ID ต่อคำขอ touch | 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 และส่งชุดที่ใหญ่ขึ้นแต่น้อยครั้งลง |