استيراد المستندات
يبيّن هذا الدليل كيف تضع مستنداتك في Sursa باستخدام واجهة البطاقات: بضعة مستندات من نص برمجي، أو كل مستندات نظام ما عبر مهمة دورية. يصبح كل مستند بطاقة في مجموعة. يجدها الناس في البحث، ويجيب المساعد منها، ولها مسؤول ووسوم وتوثيق وصلاحيات مثل أي بطاقة أخرى.
إذا أردت أن يستنتج Sursa أي المستندات حُذفت في نظامك، أو كنت ترسل المستندات من منصة تكامل، فاستخدم مصدرًا مخصصًا بدلًا من ذلك. فهو مصمم للمزامنة الكاملة والتزايدية. أما هذا الدليل فيغطي الكتابة المباشرة في مجموعة.
تحتاج إلى مفتاح API له النطاق cards:write ودور يستطيع الكتابة في المجموعة المستهدفة (editor أو admin).
وتستخدم الأمثلة الدالة المساعدة من البدء السريع.
اختيار نقطة النهاية المناسبة
| ما لديك | ما تستخدمه |
|---|---|
| سجل بمعرّف ثابت في نظامك | PUT /v1/cards-by-external-id/{external_id}: إنشاء أو استبدال بمعرّفك. |
| نص تضيفه مرة واحدة، بلا معرّف خاص بك | POST /v1/cards/text |
| ملف تضيفه مرة واحدة | POST /v1/cards |
| أرشيف ZIP لملفات كثيرة | POST /v1/cards/archive |
| ملف أكبر من حد الطلب الواحد | الرفع على أجزاء، ثم أي من نقاط النهاية أعلاه. |
| محتوى جديد لبطاقة موجودة لديك | PUT /v1/cards/{card_id}/content |
لكل ما تستورده أكثر من مرة، استخدم external_id. فهو يجعل كل استيراد قابلًا للتكرار: تشغيله مرة أخرى يحدّث البطاقات
التي أنشأها بدل إضافة نسخ.
إنشاء بطاقة أو استبدالها بمعرّفك
تأخذ العملية PUT /v1/cards-by-external-id/{external_id} معرّف السجل في نظامك. فإن لم تكن هناك بطاقة لها قيمة
external_id هذه، تنشئ واحدة (201). وإن وُجدت، تستبدل محتوى البطاقة وتفاصيلها (200).
curl -X PUT https://api.sursa.com/v1/cards-by-external-id/kb-article-1042 \
-H "Authorization: Bearer $SURSA_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"collection_id": 3,
"folder_id": "0f8e2d7c-4b6a-4e1f-9c3d-2a5b8e7f1c40",
"title": "Returning a damaged item",
"content_type": "html",
"body": "<h2>Damaged items</h2><p>Send a photo within 7 days and we ship a replacement.</p>",
"tags": ["returns", "shipping"],
"metadata": {"source": "helpdesk", "article_id": "1042"}
}'ما يحدث عند التكرار:
- لم يتغيّر المحتوى: لا يُنشأ إصدار جديد. الاستجابة
200مع"status": "skipped"و"reason": "unchanged". وتكرار الطلب آمن دائمًا، حتى بلاIdempotency-Key. - تغيّر المحتوى: تحصل البطاقة على إصدار جديد وتُفهرَس من جديد. ويأخذ عنوانها ووسومها والمسؤول عنها وفئتها
و
metadataالقيم التي أرسلتها؛ ويُستبدلmetadataكاملًا. - المجموعة والمجلد لا يُطبَّقان إلا عند إنشاء البطاقة. ولنقل البطاقة لاحقًا، غيّرها بالعملية
PATCH /v1/cards/{card_id}.
الإصدار الجديد يزيل توثيق البطاقة، كما يفعل أي تغيير في المحتوى، فيعيد موثِّق توثيقها.
صيغ النص
content_type | نص الطلب |
|---|---|
markdown (افتراضي) | بصيغة Markdown. |
html | بصيغة HTML. يُنظَّف حتى لا يبقى إلا ترميز بسيط: العناوين والفقرات والقوائم والجداول وروابط http وhttps وmailto. وتُزال النصوص البرمجية والأنماط والإطارات والاستمارات والصور مع محتواها. |
text | نص عادي. تُفصل الفقرات بأسطر فارغة. |
انتظار البطاقة أو عدمه
افتراضيًا، يعود الطلب حين تصبح البطاقة جاهزة للعثور عليها، مع "status": "ready" والبطاقة في card. ويستغرق ذلك
من جزء من الثانية للنص القصير إلى دقيقة أو أكثر للملف الطويل.
في الاستيراد الجماعي، أرسل "async": true. فتصل الاستجابة فورًا مع "status": "queued" ومهمة الفهرسة في job
(مع job_id). تحقّق من المهمة بالعملية GET /v1/jobs/{job_id}، أو تابع كل المهام في
GET /v1/jobs/stream. وتنتهي المهمة بقيمة status هي done أو error أو
canceled.
curl https://api.sursa.com/v1/jobs/48213 \
-H "Authorization: Bearer $SURSA_API_KEY"رفع ملف
تُرسَل الملفات في طلب multipart/form-data، في الحقل file. يقرأ Sursa ملفات PDF وWord وExcel وPowerPoint وجداول
OpenDocument وCSV وMarkdown وHTML وAsciiDoc والبريد الإلكتروني (.eml) والنص العادي وصيغ الصور الشائعة. ويتحقق من
تطابق اسم الملف ونوعه المعلن ومحتواه، ويرفض عدم التطابق بالحالة 415.
لإنشاء بطاقة ملف أو استبدالها بمعرّفك، أرسل الملف إلى العملية نفسها PUT /v1/cards-by-external-id/{external_id}:
curl -X PUT https://api.sursa.com/v1/cards-by-external-id/handbook-2026 \
-H "Authorization: Bearer $SURSA_API_KEY" \
-F "file=@employee-handbook.pdf" \
-F "collection_id=3" \
-F "tags=hr,policies" \
-F 'metadata={"owner_team":"people"}'في الاستمارة، الحقل tags سلسلة نصية مفصولة بفواصل، وmetadata كائن JSON مكتوب سلسلةً نصية. والبطاقة الجديدة بلا
title تأخذ اسم الملف عنوانًا لها.
لإضافة ملف مرة واحدة بلا معرّف خاص بك، استخدم POST /v1/cards بالاستمارة نفسها مع
Idempotency-Key. ورفع ملف موجود من قبل في المجموعة يعيد "status": "skipped" مع "duplicate": true وcard_id
الموجود، ما لم تمرّر external_id.
حجم الملف الواحد محدود بإعدادات التثبيت (200 ميغابايت افتراضيًا). وللملفات الأكبر، أو للشبكات غير الموثوقة، ارفع الملف على أجزاء أولًا.
استبدال محتوى بطاقة
حين يكون لديك معرّف البطاقة في Sursa، تستبدل العملية PUT /v1/cards/{card_id}/content
نصها أو ملفها وتُبقي كل ما عداه: المسؤول، والوسوم، والموثِّقين، وفترة التوثيق، والفئة المعيّنة يدويًا.
curl -X PUT https://api.sursa.com/v1/cards/3f2c9a7e-5b1d-4c8e-9f0a-1b2c3d4e5f60/content \
-H "Authorization: Bearer $SURSA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"content_type": "markdown", "body": "# Refund policy\n\nRefunds are processed within 10 days."}'- إرسال المحتوى نفسه مرة أخرى يعيد
"status": "skipped"مع"reason": "unchanged". - البطاقة التي تجري فهرستها الآن تعيد الحالة
409مع الرمزcards.indexing_in_progress. أعد المحاولة بعد بضع ثوانٍ. - البطاقة الآتية من مصدر، مثل موصّل أو مصدر مخصص، لا تُغيَّر إلا في مصدرها:
cards.managed_by_source. - يجب أن يُسمح للمفتاح برؤية البطاقة كلها؛ والبطاقة المرئية جزئيًا تعيد
cards.content_whole_access.
استيراد مستندات كثيرة
حلقة تمر على سجلاتك مع PUT /v1/cards-by-external-id/… هي أبسط استيراد جماعي، ويمكن إيقافها وتشغيلها من جديد بأمان
في أي لحظة. ولجعلها سريعة ومراعية لحد المعدل:
- أرسل
"async": true، كي يعود كل طلب بمجرد وضع البطاقة في قائمة الانتظار. - شغّل بضعة طلبات بالتوازي (من 4 إلى 8)، وأبطئ حين تُظهر الترويسة
RateLimitأن الطلبات المتبقية قليلة. انظر حدود معدل الطلبات والحصص. - عند الحالة
429، انتظرRetry-After؛ وعند خطأ في الشبكة أو5xx، أعد الطلب نفسه. فالتكرار لا ضرر منه. - دوّن
external_idلكل سجل ينتهي بخطأ، وأعد محاولته في النهاية.
import { api } from "./sursa.mjs";
async function importAll(records, concurrency = 6) {
const failed = [];
const queue = [...records];
async function worker() {
for (let record = queue.shift(); record; record = queue.shift()) {
try {
await api("PUT", `/v1/cards-by-external-id/${encodeURIComponent(record.id)}`, {
collection_id: 3,
title: record.title,
content_type: "html",
body: record.html,
metadata: { source: "helpdesk" },
async: true,
});
} catch (error) {
failed.push({ id: record.id, error: String(error) });
}
}
}
await Promise.all(Array.from({ length: concurrency }, worker));
return failed;
}لأرشيف ZIP من الملفات، تحوّل العملية POST /v1/cards/archive كل ملف مدعوم
فيه إلى بطاقة بطلب واحد. وتجيب فورًا بخطة الاستيراد (أي الملفات تصبح بطاقات وأيها يُتخطى، ولماذا) وبمهمة معالجة؛
وتعرض العملية GET /v1/archives/{job_id} النتيجة.
العثور على البطاقات المستوردة وحذفها
- اقرأ بطاقة بمعرّفك بالعملية
GET /v1/cards-by-external-id/{external_id}. - اسرد بطاقات استيراد واحد بعامل تصفية على البيانات الوصفية:
GET /v1/cards?metadata[source]=helpdesk. انظر البيانات الوصفية. - احذف بطاقة بالعملية
DELETE /v1/cards/{card_id}.
الأخطاء الشائعة
| الرمز | السبب | ما العمل |
|---|---|---|
collections.not_found | لا مجموعة بهذا المعرّف أو الاسم مرئية للمفتاح. | تحقّق من collection_id بالعملية GET /v1/collections. |
collections.no_write_access | لا يحق للمفتاح الكتابة في هذه المجموعة. | استخدم مفتاحًا بالدور editor وله وصول إلى المجموعة. |
cards.external_id_taken | طلب POST مع external_id تحمله بطاقة أخرى. | استخدم PUT /v1/cards-by-external-id/… لاستبدالها. |
cards.managed_by_source | البطاقة تتبع مصدرًا. | غيّر المستند في مصدره. |
files.type_mismatch | اسم الملف ونوعه ومحتواه غير متطابقة. | أرسل الملف بامتداده الحقيقي. |
files.too_large | الملف فوق حد الرفع أو فوق مساحة التخزين للمؤسسة. | استخدم الرفع على أجزاء، أو حرّر مساحة تخزين. |
limits.cards | بلغت المؤسسة حد البطاقات في خطتها. | احذف البطاقات التي لم تعد لازمة، أو غيّر الخطة. |
api.metadata_invalid | الحقل metadata يتجاوز أحد الحدود. | انظر البيانات الوصفية. |