ข้ามไปยังเนื้อหา
Sursa Developers

ซิงก์ระบบของคุณด้วยแหล่งข้อมูลแบบกำหนดเอง

แหล่งข้อมูลแบบกำหนดเอง เชื่อมต่อระบบที่ไม่มีคอนเนกเตอร์ในตัว เช่น วิกิ ระบบจัดการทิกเก็ต ฐานข้อมูล หรือข้อมูลที่ส่งออกมาจากที่ใดก็ได้ โค้ดของคุณส่งเอกสารไปยัง Sursa แล้ว Sursa จะเปลี่ยนเอกสารเหล่านั้นเป็นการ์ดในคอลเลกชันและโฟลเดอร์ที่ตั้งค่าไว้สำหรับแหล่งข้อมูลนั้น โดยมีการค้นหา คำตอบ การตรวจสอบ และสิทธิ์เหมือนการ์ดจากแหล่งข้อมูลอื่นทุกประการ

แหล่งข้อมูลแบบกำหนดเองต่างจาก การเขียนการ์ดโดยตรง ตรงที่ติดตามว่าคุณส่งอะไรไปแล้วบ้าง เมื่อจบการซิงก์แบบเต็ม Sursa จะเก็บเอกสารทุกฉบับที่คุณไม่ได้ส่งเข้าคลัง การลบในระบบของคุณจึงไปถึง Sursa โดยไม่ต้องทำอะไรเพิ่ม

ตั้งค่าแหล่งข้อมูล

ผู้ดูแลระบบสร้างแหล่งข้อมูลใน Sursa ที่ Sources → New source → Custom source (แหล่งข้อมูล → แหล่งข้อมูลใหม่ → แหล่งข้อมูลแบบกำหนดเอง) และเลือก:

  • คอลเลกชัน และโฟลเดอร์สำหรับการ์ดของแหล่งข้อมูล ซึ่งจะระบุหรือไม่ก็ได้
  • โหมดโฟลเดอร์ คือให้การ์ดทั้งหมดอยู่ในโฟลเดอร์ที่เลือก หรือให้มีโฟลเดอร์ย่อยตาม path ของเอกสารแต่ละฉบับ
  • สิทธิ์ คือใช้สิทธิ์ของคอลเลกชัน หรือ “ตามแหล่งข้อมูล” ซึ่งเอกสารแต่ละฉบับระบุว่าใครมองเห็นได้บ้าง

หน้าแหล่งข้อมูลแสดง ID ของแหล่งข้อมูล ตัวอย่างใช้ 42

เลือกคีย์

คีย์ขึ้นต้นด้วยทำอะไรได้
คีย์แหล่งข้อมูลkbsrc_เขียนเอกสารลงในแหล่งข้อมูลนี้แหล่งเดียว ไม่มีสิทธิ์อื่น อ่านหรือค้นหาไม่ได้ สร้างได้ในหน้าแหล่งข้อมูลที่ส่วน API keys (คีย์ API)
คีย์ขององค์กรที่มีขอบเขตสิทธิ์ import:writekb_เขียนลงในแหล่งข้อมูลแบบกำหนดเองใดก็ได้ขององค์กรและอ่านสถานะของแหล่งข้อมูลนั้น การสร้างแหล่งข้อมูลและคีย์ของแหล่งข้อมูลต้องใช้ขอบเขตสิทธิ์ 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
  • เปิดการซิงก์ได้ครั้งละหนึ่งรายการเท่านั้น การเปิดรายการที่สองจะได้ 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 (เอกสารอื่นในชุดแบบอะตอมิกล้มเหลว)

โดยค่าเริ่มต้น เอกสารแต่ละฉบับในชุดจะถูกนำไปใช้แยกกัน เอกสารที่ผิดพลาดหนึ่งฉบับจึงไม่หยุดฉบับอื่น

เอกสารที่ไม่เปลี่ยนแปลงตั้งแต่การซิงก์ครั้งก่อนไม่ต้องส่งซ้ำ ให้ยืนยันเอกสารเหล่านั้นด้วย 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} ส่งคืนตัวนับชุดเดียวกัน

การซิงก์แบบส่วนเพิ่ม

ระหว่างการซิงก์แบบเต็มแต่ละครั้ง ให้ส่งเฉพาะสิ่งที่เปลี่ยนแปลง:

  1. เปิดด้วย "type": "incremental"
  2. ส่งเอกสารที่เปลี่ยนแปลง และใส่ ID ของเอกสารที่ถูกนำออกไว้ใน deletes (ด้านล่าง)
  3. ปิดด้วย 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 ต่อคำขอ touch1000
จำนวนเอกสารต่อแหล่งข้อมูลขึ้นอยู่กับแพ็กเกจ ตั้งแต่ 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 และส่งชุดที่ใหญ่ขึ้นแต่น้อยครั้งลง