मुख्य कंटेंट पर जाएँ
Sursa Developers

कस्टम स्रोत से अपने सिस्टम का सिंक

कस्टम स्रोत ऐसे सिस्टम को जोड़ता है, जिसके लिए कोई बिल्ट-इन कनेक्टर नहीं है: आपकी विकी, आपका टिकटिंग टूल, कोई डेटाबेस, कहीं से भी किया गया एक्सपोर्ट। आपका कोड दस्तावेज़ Sursa में भेजता है, और Sursa उन्हें उस कलेक्शन और उन फ़ोल्डर में कार्ड बना देता है, जिनके लिए स्रोत सेट किया गया है, उसी खोज, जवाबों, सत्यापन और अनुमतियों के साथ, जो किसी भी दूसरे स्रोत के कार्ड के लिए हैं।

सीधे कार्ड लिखने से अलग, कस्टम स्रोत याद रखता है कि आपने क्या भेजा है। पूरे सिंक के आख़िर में Sursa हर वह दस्तावेज़ आर्काइव कर देता है, जो आपने नहीं भेजा, इसलिए आपके सिस्टम में हुए डिलीशन बिना अतिरिक्त काम के Sursa तक पहुँच जाते हैं।

स्रोत सेट करें

एडमिन Sursa में Sources → New source → Custom source (स्रोत → नया स्रोत → कस्टम स्रोत) में स्रोत बनाता है, और चुनता है:

  • कलेक्शन और, चाहें तो, उसके कार्ड के लिए एक फ़ोल्डर;
  • फ़ोल्डर मोड: सभी कार्ड चुने गए फ़ोल्डर में, या ऐसे सब-फ़ोल्डर, जो हर दस्तावेज़ के path को दोहराते हैं;
  • अनुमतियाँ: कलेक्शन की अनुमतियाँ, या “स्रोत की तरह”, जहाँ हर दस्तावेज़ बताता है कि उसे कौन देख सकता है।

स्रोत के पेज पर उसकी ID दिखती है; उदाहरण 42 इस्तेमाल करते हैं।

कुंजी चुनें

कुंजीकिससे शुरू होती हैक्या कर सकती है
स्रोत कुंजीkbsrc_इसी एक स्रोत में दस्तावेज़ लिखना। और कुछ नहीं: यह न पढ़ सकती है, न खोज सकती है। इसे स्रोत के पेज पर API keys (स्रोत की API कुंजियाँ) में बनाएँ।
import:write स्कोप वाली संगठन की कुंजीkb_संगठन के किसी भी कस्टम स्रोत में लिखना और उसकी स्थिति पढ़ना। स्रोत और उसकी कुंजियाँ बनाने के लिए स्कोप 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हाँदस्तावेज़ की आपकी स्थिर 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 के बजाय एक अकेला दस्तावेज़ multipart/form-data रिक्वेस्ट में 100 MB तक की फ़ाइल ले जा सकता है: फ़ाइल 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}एक दस्तावेज़ हटाती है। 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 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 लगभग एक मिनट के अंदर दस्तावेज़ों को कार्ड बना देता है। स्रोत का पेज हर सिंक को भेजे गए और आर्काइव किए गए दस्तावेज़ों की संख्या के साथ दिखाता है, और 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 भेजें, और बैच या तो पूरा लागू होता है या बिल्कुल नहीं: अगर कोई भी दस्तावेज़ अपनी जाँच में असफल होता है, तो बैच का कुछ भी लागू नहीं होता, और रिस्पॉन्स कोड sources.push_batch_rejected के साथ 422 होता है, और 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 के साथ) वेबहुक से और इवेंट फ़ीड में आते हैं।

रीट्राई

  • हर POST के साथ Idempotency-Key भेजें। उसी कुंजी के साथ दोहराया गया बैच पहला नतीजा लौटाता है।
  • कुंजी के बिना भी उसी external_id वाला दस्तावेज़ कभी दूसरा कार्ड नहीं बनता: वह unchanged या updated होता है।
  • अगर प्रोसेस सिंक के बीच में रुक जाए, तो पुराने सिंक की मियाद ख़त्म होने के बाद नया खोलें, या अगर आपके पास पुराने का नंबर है, तो उसे failed से बंद करें, और सिंक फिर से चलाएँ।

सीमाएँ

सीमामान
एक दस्तावेज़ का कंटेंट5 MB
एक दस्तावेज़ वाली रिक्वेस्ट में फ़ाइल100 MB
हर बैच में दस्तावेज़500
बैच रिक्वेस्ट की बॉडी50 MiB
हर touch रिक्वेस्ट में ID1000
हर स्रोत में दस्तावेज़प्लान पर निर्भर: 100,000 से
खुला सिंकडिफ़ॉल्ट रूप से 60 मिनट, ज़्यादा से ज़्यादा 24 घंटे

स्रोत की दस्तावेज़ सीमा से ऊपर का दस्तावेज़ बैच में limit_reached कारण के साथ असफल होता है; एक दस्तावेज़ वाली रिक्वेस्ट कोड sources.push_too_many_documents के साथ 409 लौटाती है। रेट लिमिट किसी भी दूसरी रिक्वेस्ट की तरह लागू होती हैं; देखें रेट लिमिट और कोटा।

आम एरर

जवाबकारणक्या करें
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 तक इंतज़ार करें; कम, पर बड़े बैच भेजें।