انتقل إلى المحتوى
Sursa Developers

مزامنة نظامك عبر مصدر مخصص

يربط المصدر المخصص نظامًا ليس له موصّل مدمج: الويكي لديك، أو أداة التذاكر، أو قاعدة بيانات، أو تصديرًا من أي مكان. ترسل شيفرتك المستندات إلى Sursa، فيحوّلها Sursa إلى بطاقات في المجموعة والمجلدات التي ضُبط المصدر لها، مع البحث والإجابات والتوثيق والصلاحيات نفسها التي لبطاقات أي مصدر آخر.

وبخلاف كتابة البطاقات مباشرة، يتتبع المصدر المخصص ما أرسلته. ففي نهاية المزامنة الكاملة، يؤرشف Sursa كل مستند لم ترسله، فتصل عمليات الحذف في نظامك إلى Sursa بلا عمل إضافي.

إعداد المصدر

ينشئ المشرف المصدر في Sursa من Sources → New source → Custom source (المصادر ← مصدر جديد ← مصدر مخصص)، ويختار:

  • المجموعة، ومجلدًا لبطاقاتها إن شاء؛
  • وضع المجلدات: كل البطاقات في المجلد المختار، أو مجلدات فرعية تعكس path كل مستند؛
  • الصلاحيات: صلاحيات المجموعة، أو «كما في المصدر»، حيث يذكر كل مستند من يحق له رؤيته.

تعرض صفحة المصدر معرّفه، والأمثلة تستخدم 42.

اختيار المفتاح

المفتاحالبادئةما يستطيعه
مفتاح المصدرkbsrc_كتابة المستندات في هذا المصدر الواحد. ولا شيء غير ذلك: لا يستطيع القراءة ولا البحث. أنشئه في صفحة المصدر من API keys (مفاتيح API للمصدر).
مفتاح المؤسسة ذو النطاق import:writekb_الكتابة في أي مصدر مخصص للمؤسسة وقراءة حالته. ويتطلب إنشاء مصدر ومفاتيحه النطاق 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نعممعرّفك الثابت للمستند، حتى 512 حرفًا. وإرسال المعرّف نفسه مرة أخرى يستبدل المستند.
titleنعمعنوان البطاقة، حتى 500 حرف. وللملف، اسم الملف افتراضيًا.
contentنعم، ما لم يكن هناك ملفحتى 5 ميغابايت.
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 ميغابايت في طلب 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 على المعرّف.

تصبح التغييرات بطاقات خلال دقيقة تقريبًا، وتُعالَج التغييرات المتتالية معًا.

المزامنات تناسب التصديرات الدورية: ترسل كل شيء، أو كل ما تغيّر، ويُجري 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 ميبيبايت؛ والملفات داخل الدفعة تُمرَّر على شكل 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، حتى 1000 معرّف في الطلب الواحد، كي تُبقي عليها المطابقة الكاملة:

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. أرسل المستندات المتغيرة، وضع معرّفات المحذوفة في deletes (أدناه).
  3. أغلق بالقيمة complete_incremental.

شغّل مزامنة كاملة من حين لآخر، أسبوعيًا مثلًا، لالتقاط ما فات المزامنة التزايدية.

حذف المستندات في دفعة

يمكن للدفعة أن تزيل مستندات مع المستندات التي تكتبها. اسرد معرّفاتها في deletes:

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

لكل معرّف محذوف إدخاله الخاص في results، بقيمة result هي deleted، أو not_found إن لم يكن في المصدر مستند كهذا. وحذف المعرّف نفسه مرة أخرى لا ضرر منه.

الدفعات الذرية

بعض التغييرات لا معنى لها إلا معًا، مثل مستند والمستند الذي يحل محله. أرسل "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 ميغابايت
الملف في طلب المستند الواحد100 ميغابايت
المستندات في الدفعة500
نص طلب الدفعة50 ميبيبايت
المعرّفات في طلب touch1000
المستندات في المصدربحسب الخطة: من 100,000
المزامنة المفتوحة60 دقيقة افتراضيًا، وحتى 24 ساعة

المستند الذي يتجاوز حد المستندات في المصدر يفشل بالسبب limit_reached في الدفعة؛ أما طلب المستند الواحد فيجيب بالحالة 409 مع الرمز sources.push_too_many_documents. وتسري حدود المعدل كما في أي طلب آخر؛ انظر حدود معدل الطلبات والحصص.

الأخطاء الشائعة

الاستجابةالسببما العمل
401 sources.push_key_invalidالمفتاح خاطئ أو مُبطَل أو يخص مصدرًا آخر.تحقّق من المفتاح ومن معرّف المصدر في المسار.
400 sources.push_document_invalidحقل مطلوب ناقص أو تجاوز لأحد الحدود.أصلح المستند؛ في الدفعة العادية لا يفشل إلا ذلك المستند.
409 sources.push_sync_openمزامنة أخرى لا تزال مفتوحة.أغلقها، أو انتظر حتى تنتهي صلاحيتها.
409 sources.push_sync_closedالمزامنة مغلقة أو انتهت صلاحيتها.افتح مزامنة جديدة؛ وارفع max_minutes للتصديرات الطويلة.
403 sources.feature_disabledالمصادر ليست جزءًا من خطة المؤسسة.تواصل مع المشرف.
429طلبات كثيرة جدًا.انتظر Retry-After؛ وأرسل دفعات أقل عددًا وأكبر حجمًا.